Socket与系统调用深度分析

        上次Linux内核的实验,这次我们要在上次的基础上,做一个Socket与系统调用的深度分析。本次实验在X86 64环境下Linux5.0.1的内核中进一步跟踪验证。

        首先,上次实验我们用vim更改了menu文件夹下的Makefile文件中的一行,这次实验我们还需要对其做更改,删除那一行最后的-S因为这个命令会在qemu打开时暂时挂起在cpu,别忘了:wq保存。

 

 

 

 

 

 

 

      

 

 

 

 

 

 

 

 

 

 

 

        改完就make rootfs,出现menuos。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

        和上次实验一样,不要关闭这个终端和qemu窗口,另开一终端,gdb调试。

 

cd ../linux-5.0.1
gdb file .
/vmlinux target remote:1234
b __ia32_compat_sys_socketcall b __x64_sys_bind b __x64_sys_listen
b __x64_sys_connect
b __x64_sys_accept4

 

        

 

        通过截图,可以看出我们在gdb中分别找到了函数的路径信息。根据信息,我们找到函数的定义。

__ia32_compat_sys_socketcall实际的函数是compat.c中838行的COMPAT_SYSCALL_DEFINE2,定义如下:

COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
{
    u32 a[AUDITSC_ARGS];
    unsigned int len;
    u32 a0, a1;
    int ret;

    if (call < SYS_SOCKET || call > SYS_SENDMMSG)
        return -EINVAL;
    len = nas[call];
    if (len > sizeof(a))
        return -EINVAL;

    if (copy_from_user(a, args, len))
        return -EFAULT;

    ret = audit_socketcall_compat(len / sizeof(a[0]), a);
    if (ret)
        return ret;

    a0 = a[0];
    a1 = a[1];

    switch (call) {
    case SYS_SOCKET:
        ret = __sys_socket(a0, a1, a[2]);
        break;
    case SYS_BIND:
        ret = __sys_bind(a0, compat_ptr(a1), a[2]);
        break;
    case SYS_CONNECT:
        ret = __sys_connect(a0, compat_ptr(a1), a[2]);
        break;
    case SYS_LISTEN:
        ret = __sys_listen(a0, a1);
        break;
    case SYS_ACCEPT:
        ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), 0);
        break;
    case SYS_GETSOCKNAME:
        ret = __sys_getsockname(a0, compat_ptr(a1), compat_ptr(a[2]));
        break;
    case SYS_GETPEERNAME:
        ret = __sys_getpeername(a0, compat_ptr(a1), compat_ptr(a[2]));
        break;
    case SYS_SOCKETPAIR:
        ret = __sys_socketpair(a0, a1, a[2], compat_ptr(a[3]));
        break;
    case SYS_SEND:
        ret = __sys_sendto(a0, compat_ptr(a1), a[2], a[3], NULL, 0);
        break;
    case SYS_SENDTO:
        ret = __sys_sendto(a0, compat_ptr(a1), a[2], a[3],
                   compat_ptr(a[4]), a[5]);
        break;
    case SYS_RECV:
        ret = __compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
                        NULL, NULL);
        break;
    case SYS_RECVFROM:
        ret = __compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
                        compat_ptr(a[4]),
                        compat_ptr(a[5]));
        break;
    case SYS_SHUTDOWN:
        ret = __sys_shutdown(a0, a1);
        break;
    case SYS_SETSOCKOPT:
        ret = __compat_sys_setsockopt(a0, a1, a[2],
                          compat_ptr(a[3]), a[4]);
        break;
    case SYS_GETSOCKOPT:
        ret = __compat_sys_getsockopt(a0, a1, a[2],
                          compat_ptr(a[3]),
                          compat_ptr(a[4]));
        break;
    case SYS_SENDMSG:
        ret = __compat_sys_sendmsg(a0, compat_ptr(a1), a[2]);
        break;
    case SYS_SENDMMSG:
        ret = __compat_sys_sendmmsg(a0, compat_ptr(a1), a[2], a[3]);
        break;
    case SYS_RECVMSG:
        ret = __compat_sys_recvmsg(a0, compat_ptr(a1), a[2]);
        break;
    case SYS_RECVMMSG:
        ret = __sys_recvmmsg(a0, compat_ptr(a1), a[2],
                     a[3] | MSG_CMSG_COMPAT, NULL,
                     compat_ptr(a[4]));
        break;
    case SYS_ACCEPT4:
        ret = __sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]);
        break;
    default:
        ret = -EINVAL;
        break;
    }
    return ret;
}

 

__sys_blind代码:

int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
{
    struct socket *sock;
    struct sockaddr_storage address;
    int err, fput_needed;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        err = move_addr_to_kernel(umyaddr, addrlen, &address);
        if (!err) {
            err = security_socket_bind(sock,
                           (struct sockaddr *)&address,
                           addrlen);
            if (!err)
                err = sock->ops->bind(sock,
                              (struct sockaddr *)
                              &address, addrlen);
        }
        fput_light(sock->file, fput_needed);
    }
    return err;
}

        

__sys_listen代码:

int __sys_listen(int fd, int backlog)
{
    struct socket *sock;
    int err, fput_needed;
    int somaxconn;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
        if ((unsigned int)backlog > somaxconn)
            backlog = somaxconn;

        err = security_socket_listen(sock, backlog);
        if (!err)
            err = sock->ops->listen(sock, backlog);

        fput_light(sock->file, fput_needed);
    }
    return err;
}

        剩下函数代码不再赘述,总之,这两个例子是说明我们通过断点可以追踪到函数定义。 

        接下来一次次执行c命令,第一次执行后在qemu中输入replyhi,qemu又停止:

 

 

 

         接着执行c,直到qemu再次运行:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

        直到__sys_accept4执行过后,才能继续输入hello,说明replyhi的执行一直到accept4,并且,我们可以看到尽管为connect设置了断点,但是却没有出现conect函数的中断,这是因为在这个过程中没有执行__sys_connect函数,我们只运行了一个menuos。我们为使用系统调用的__COMPAT_SYSCALL_DEFINE设置了断点,在执行__sys_bind,__sys_listen,__sys_acept4之前,明显看到这个函数都执行了一次,显然,套接口的bind,listen,accept功能都使用了系统调用。据此,我猜套接口绝大部分功能的实现都借用了系统调用。

        通过这次实验,我懂得了gdb调试真是太好用了,可以很方便的观察程序运行的状态,并且对系统调用,socket套接口有了更深地认识。本次实验运用到了套接口的各种知识,比如套接口各个功能对应的函数等,还有系统调用的概念,gdb的各种命令的功能,这些东西都是随处可见的基本知识,不再赘述。

        

 

 

 

 

 

 

 

 

 

 

    

posted @ 2019-12-19 20:15  rings  阅读(294)  评论(0编辑  收藏  举报