如果user_mode(regs)返回1,接下来会执行(中间略去一下和本文关系不大的代码)get_signal_to_deliver(),这个函数从当前进程的信号队列(保存Private Signal Queue和Shared Signal Queue)取出等待处理的信号(调用dequeue_signal()函数),然后根据信号定位到对应的signal_struct结构,如果信号的处理函数sa_handler为SIG_IGN,就忽略该信号,继续取下一个信号;如果信号的处理函数sa_handler为SIG_DFL,意味着按照信号默认的处理方式对待就可以了(例如直接调用do_coredump()等)。
如果get_signal_to_deliver()函数返回值大于0,说明这个信号的处理函数是在用户态空间(通过signal()和sigaction()等函数设置的自定义信号处理函数。),将调用handle_signal()函数进行处理。handle_signal()函数的定义如下:
/* /* /* /* /* /* if (ret == 0) force_sigsegv(sig, tsk); |
在这样情况下,进程当前处于内核态,而信号处理函数却处于用户态,为此必须在进程的用户态构造一个临时的堆栈环境(因为进程的信号处理函数在进行函数调用以及使用局部变量时需要使用堆栈。),然后进入用户态执行信号处理函数,最后再返回内核态继续执行。在这个过程中,有以下几个问题需要解决:
1、临时的用户态堆栈在哪里呢?这个很好解决,因为可以直接使用进程现有的用户态堆栈,这里要保证的是:使用结束后这个堆栈必须和使用前是一模一样的。
2、临时堆栈解决后,需要确定的是通过什么方法来保证返回到用户态后,进程执行的是信号的处理函数。我们知道在进入内核态时,内核态堆栈中保存了一个中断现场,也就是一个pt_regs结构,中断返回地址就保存在pt_regts中的pc中,因此我们这里只要把当前进程的pt_regs中pc设置为sa_handler,然后返回到用户态就开始从sa_handler处开始执行了。
unsigned long handler = (unsigned long)ka->sa.sa_handler; regs->ARM_pc = handler; |