美文网首页
day14 signal

day14 signal

作者: 柯基是只dog | 来源:发表于2018-12-12 18:01 被阅读0次

    信号是一种在进程间异步相应的方式,进程可以通过系统调用设置不同信号的相应回调,每次调度之前内核都会检查进程的信号,如果有置位的信号,则会唤醒该进程,下面两个是设置信号的调用

    // signal()系统调用。类似于sigaction()。为指定的信号安装新的信号句柄(信号处理程序)。
    // 信号句柄可以是用户指定的函数,也可以是SIG_DFL(默认句柄)或SIG_IGN(忽略)。
    // 参数signum --指定的信号;handler -- 指定的句柄;restorer –原程序当前执行的地址位置。
    // 函数返回原信号句柄。
    int sys_signal (int signum, long handler, long restorer)
    {
        struct sigaction tmp;
    
        if (signum < 1 || signum > 32 || signum == SIGKILL) // 信号值要在(1-32)范围内,
            return -1;          // 并且不得是SIGKILL。
        tmp.sa_handler = (void (*)(int)) handler;   // 指定的信号处理句柄。
        tmp.sa_mask = 0;        // 执行时的信号屏蔽码。
        tmp.sa_flags = SA_ONESHOT | SA_NOMASK;  // 该句柄只使用1 次后就恢复到默认值,
    // 并允许信号在自己的处理句柄中收到。
        tmp.sa_restorer = (void (*)(void)) restorer;    // 保存返回地址。
        handler = (long) current->sigaction[signum - 1].sa_handler;
        current->sigaction[signum - 1] = tmp;
        return handler;
    }
    
    // sigaction()系统调用。改变进程在收到一个信号时的操作。signum 是除了SIGKILL 以外的任何
    // 信号。[如果新操作(action)不为空]则新操作被安装。如果oldaction 指针不为空,则原操作
    // 被保留到oldaction。成功则返回0,否则为-1。
    int sys_sigaction (int signum, const struct sigaction *action,
                        struct sigaction *oldaction)
    {
        struct sigaction tmp;
    
    // 信号值要在(1-32)范围内,并且信号SIGKILL 的处理句柄不能被改变。
        if (signum < 1 || signum > 32 || signum == SIGKILL)
            return -1;
    // 在信号的sigaction 结构中设置新的操作(动作)。
        tmp = current->sigaction[signum - 1];
        get_new ((char *) action, (char *) (signum - 1 + current->sigaction));
    // 如果oldaction 指针不为空的话,则将原操作指针保存到oldaction 所指的位置。
        if (oldaction)
            save_old ((char *) &tmp, (char *) oldaction);
    // 如果允许信号在自己的信号句柄中收到,则令屏蔽码为0,否则设置屏蔽本信号。
        if (current->sigaction[signum - 1].sa_flags & SA_NOMASK)
            current->sigaction[signum - 1].sa_mask = 0;
        else
            current->sigaction[signum - 1].sa_mask |= (1 << (signum - 1));
        return 0;
    }
    

    然后信号处理函数有点意思,因为是在内核态本来是要返回到用户态并从中断点重新开始运行的。但内核如果检测到需要处理信号的时候,就会把eip改成信号处理入口地址,然后通过修改用户态的栈,把中断返回出的eip和段等必要的寄存器压入,这样当从内核恢复到用户态的时候首先执行的是信号处理函数,信号处理函数返回的时候,会从堆栈中弹出原来的地址,但这个时候已经被内核修改成了当初中断打断的地方,于是就继续从中断点开始执行了

    void do_signal (long signr, long eax, long ebx, long ecx, long edx,
                long fs, long es, long ds,
                long eip, long cs, long eflags, unsigned long *esp, long ss)
    {
        unsigned long sa_handler;
        long old_eip = eip;
        struct sigaction *sa = current->sigaction + signr - 1;  //current->sigaction[signu-1]。
        int longs;
        unsigned long *tmp_esp;
    
        sa_handler = (unsigned long) sa->sa_handler;
    // 如果信号句柄为SIG_IGN(忽略),则返回;如果句柄为SIG_DFL(默认处理),则如果信号是
    // SIGCHLD 则返回,否则终止进程的执行
        if (sa_handler == 1)
            return;
        if (!sa_handler)
        {
            if (signr == SIGCHLD)
                return;
            else
    // 这里应该是do_exit(1<<signr))。
                do_exit (1 << (signr - 1)); // [?? 为什么以信号位图为参数?不为什么!??]
        }
    // 如果该信号句柄只需使用一次,则将该句柄置空(该信号句柄已经保存在sa_handler 指针中)。
        if (sa->sa_flags & SA_ONESHOT)
            sa->sa_handler = NULL;
    // 下面这段代码将信号处理句柄插入到用户堆栈中,同时也将sa_restorer,signr,进程屏蔽码(如果
    // SA_NOMASK 没置位),eax,ecx,edx 作为参数以及原调用系统调用的程序返回指针及标志寄存器值
    // 压入堆栈。因此在本次系统调用中断(0x80)返回用户程序时会首先执行用户的信号句柄程序,然后
    // 再继续执行用户程序。
    // 将用户调用系统调用的代码指针eip 指向该信号处理句柄。
        *(&eip) = sa_handler;
    // 如果允许信号自己的处理句柄收到信号自己,则也需要将进程的阻塞码压入堆栈。
        longs = (sa->sa_flags & SA_NOMASK) ? 7 : 8;
    // 将原调用程序的用户的堆栈指针向下扩展7(或8)个长字(用来存放调用信号句柄的参数等),
    // 并检查内存使用情况(例如如果内存超界则分配新页等)。
        *(&esp) -= longs;
        verify_area (esp, longs * 4);
    // 在用户堆栈中从下到上存放sa_restorer, 信号signr, 屏蔽码blocked(如果SA_NOMASK 置位),
    // eax, ecx, edx, eflags 和用户程序原代码指针。
        tmp_esp = esp;
        put_fs_long ((long) sa->sa_restorer, tmp_esp++);
        put_fs_long (signr, tmp_esp++);
        if (!(sa->sa_flags & SA_NOMASK))
            put_fs_long (current->blocked, tmp_esp++);
        put_fs_long (eax, tmp_esp++);
        put_fs_long (ecx, tmp_esp++);
        put_fs_long (edx, tmp_esp++);
        put_fs_long (eflags, tmp_esp++);
        put_fs_long (old_eip, tmp_esp++);
        current->blocked |= sa->sa_mask;    // 进程阻塞码(屏蔽码)添上sa_mask 中的码位。
    }
    
    

    相关文章

      网友评论

          本文标题:day14 signal

          本文链接:https://www.haomeiwen.com/subject/qppphqtx.html