• Socket与系统调用深度分析


    用户态、内核态、中断和系统调用的关系

    所谓的中断就是在计算机执行程序的过程中,由于出现了某些特殊事情,使得CPU暂停对程序的执行,转而去执行处理这一事件的程序。等这些特殊事情处理完之后再回去执行之前的程序。中断一般分为三类:

    • 由计算机硬件异常或故障引起的中断,称为内部异常中断
    • 由程序中执行了引起中断的指令而造成的中断,称为软中断(系统调用就属于软中断)
    • 由外部设备请求引起的中断,称为外部中断

    进程的执行在系统上有两个级别:用户态和核心态。程序的执行一般是在用户态下执行的,但在此状态下,不能执行特权指令(访问IO设备、访问特殊寄存器、置时钟... ...)。当程序需要使用这些特权指令时,就需要向操作系统发出调用服务的请求,这就是系统调用。 Linux系统有专门的函数库来提供这些请求操作系统服务的入口,这个函数库中包含了操作系统所提供的对外服务的接口。

    系统调用也是函数调用,系统函数也是函数代码。系统函数与普通函数唯一的不同在于,系统函数可以使用cpu体系结构指令集中的特权指令,如启动I/O设备指令、修改某些个特殊寄存器的指令,如程序状态寄存器PSW。既然系统调用也是函数调用,那么就要遵守函数调用的基本法。1.传参 2.调用 3.返回。在汇编代码中,在正式call一个函数之前,需要将相关寄存器的值设置好,然后再修改pc寄存器值指向函数。上图中的eax = 2,就是在准备参数,为中断服务程序提供参数。

    用户态、内核态、系统调用和中断的关系就在于,当用户态进程发出系统调用申请的时候,会产生一个软中断。产生这个软中断以后,系统会去对这个软中断进行处理,这个时候进程就处于核心态了。

    switch (call) {
       case SYS_SOCKET:
           err = __sys_socket(a0, a1, a[2]);
           break;
       case SYS_BIND:
           err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
           break;
       case SYS_CONNECT:
           err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
           break;
       case SYS_LISTEN:
           err = __sys_listen(a0, a1);
           break;
       case SYS_ACCEPT:
           err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                       (int __user *)a[2], 0);
           break;
       case SYS_GETSOCKNAME:
           err =
               __sys_getsockname(a0, (struct sockaddr __user *)a1,
                         (int __user *)a[2]);
           break;
       case SYS_GETPEERNAME:

      INT 80进入到sys_call这个函数,这个函数实现上是switch,如果在进入sys_call这个函数时,传入102,那么就会跳转到sys_sokcetcall这个函数中去,然后我们在sys_sokcetcall函数中又看到了switch函数。根据你进入到sys_sokcetcall函数所传入的参数,将调用不同的系统函数。

    实验过程

    1.先进入menuos

    2.打开另一个终端,输入以下命令:

    gdb
    file ~/LinuxKernel/linux-5.0.1/vmlinux
    target remote:1234
    break sys_socketcall     //设置断点
    c                                //查看断点信息

    如下图所示:

    3.在menuos中输入终端命令:

    replyhi
    hello

    此时已经成功捕捉到了sys_socketcall,对应的内核处理函数为SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)

    如下图所示:

    4.根据这些系统调用返回的系统调用号,可以查看这些系统调用实现了哪些功能,我们找到socket.c文件,其源码如下:

    复制代码
    SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
    {
        unsigned long a[AUDITSC_ARGS];
        unsigned long a0, a1;
        int err;
        unsigned int len;
    
        if (call < 1 || call > SYS_SENDMMSG)
            return -EINVAL;
        call = array_index_nospec(call, SYS_SENDMMSG + 1);
    
        len = nargs[call];
        if (len > sizeof(a))
            return -EINVAL;
    
        /* copy_from_user should be SMP safe. */
        if (copy_from_user(a, args, len))
            return -EFAULT;
    
        err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
        if (err)
            return err;
    
        a0 = a[0];
        a1 = a[1];
    
        switch (call) {
        case SYS_SOCKET:
            err = __sys_socket(a0, a1, a[2]);
            break;
        case SYS_BIND:
            err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_CONNECT:
            err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
            break;
        case SYS_LISTEN:
            err = __sys_listen(a0, a1);
            break;
        case SYS_ACCEPT:
            err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                        (int __user *)a[2], 0);
            break;
        case SYS_GETSOCKNAME:
            err =
                __sys_getsockname(a0, (struct sockaddr __user *)a1,
                          (int __user *)a[2]);
            break;
        case SYS_GETPEERNAME:
            err =
                __sys_getpeername(a0, (struct sockaddr __user *)a1,
                          (int __user *)a[2]);
            break;
        case SYS_SOCKETPAIR:
            err = __sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
            break;
        case SYS_SEND:
            err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
                       NULL, 0);
            break;
        case SYS_SENDTO:
            err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
                       (struct sockaddr __user *)a[4], a[5]);
            break;
        case SYS_RECV:
            err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                         NULL, NULL);
            break;
        case SYS_RECVFROM:
            err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                         (struct sockaddr __user *)a[4],
                         (int __user *)a[5]);
            break;
        case SYS_SHUTDOWN:
            err = __sys_shutdown(a0, a1);
            break;
        case SYS_SETSOCKOPT:
            err = __sys_setsockopt(a0, a1, a[2], (char __user *)a[3],
                           a[4]);
            break;
        case SYS_GETSOCKOPT:
            err =
                __sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
                         (int __user *)a[4]);
            break;
        case SYS_SENDMSG:
            err = __sys_sendmsg(a0, (struct user_msghdr __user *)a1,
                        a[2], true);
            break;
        case SYS_SENDMMSG:
            err = __sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2],
                         a[3], true);
            break;
        case SYS_RECVMSG:
            err = __sys_recvmsg(a0, (struct user_msghdr __user *)a1,
                        a[2], true);
            break;
        case SYS_RECVMMSG:
            if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME))
                err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                             a[2], a[3],
                             (struct __kernel_timespec __user *)a[4],
                             NULL);
            else
                err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                             a[2], a[3], NULL,
                             (struct old_timespec32 __user *)a[4]);
            break;
        case SYS_ACCEPT4:
            err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                        (int __user *)a[2], a[3]);
            break;
        default:
            err = -EINVAL;
            break;
        }
        return err;
    }

    #此函数根据不同的call来进入不同的分支,从而调用不同的内核处理函数。
    #在replyhi/hello的执行过程中,涉及到了socket的建立、recv、send等,这些不同的系统调用传给SYSCALL_DEFINE2()的参数call是不同的。
  • 相关阅读:
    Entity Framework 连接低版本数据库
    Validate Disk Failover Failed
    Unable to create Azure Mobile Service: Error 500
    查看Visual Studio异常内容
    RDLC An unexpected error occurred while compiling expressions. Native compiler return value: '-1073741511'
    Redis 64 steps
    IQueryable join 的问题
    jquery 通知页面变化
    jquery 让滚动条处于div底部
    SSIS 文件系统任务无法使用变量配置目标路径
  • 原文地址:https://www.cnblogs.com/zxy1122/p/12069585.html
Copyright © 2020-2023  润新知