学习计算机科学与技术专业,喜欢计算机,喜欢linux,喜欢编程
分类: LINUX
2013-08-03 22:10:45
其中 SIGINT 是键盘中的 ctrl + c 组合键的信号。
SIGQUIT 是键盘中的 ctrl + \ 组合键的信号。
SIGTSTP 是键盘中的 ctrl + z 组合键的信号。
signal 函数是当接收第一个参数的信号时,执行第二个参数所指函数的代码。
所以当你发送上面这三个信号给这个程序时,都会执行 haha 这个函数。
信号的分发和处理是在内核态进行的,但是从上面的例子程序可以看出,信号的处理函数可能是在用户态。在这种情况下,它们是怎样进行交互的呢?信号又是怎样控制进程的呢?
当内核给进程发送一个信号时,其实是在进程所在的进程表项的信号域设置对应于该信号的位。(这个进程表项就是 PCB ——进程控制块,linux的进程控制块为一个由 task_struct 这个结构体所定义的数据结构,这个结构体在 /include/linux/sched.h 中。)
这里需要说明的是,如果信号发送给一个正在睡眠的进程,那么要看进程进入睡眠的优先级。
如果进程睡眠在可被中断的优先级上,则唤醒进程;否则仅设置进程表中信号域相应的位,而不唤醒进程。这个很重要,因为进程是通过上下文来检测是否收到信号的。具体地讲:一个进程在即将从内核态返回到用户态时;或者在进程要进入或离开一个适当的地调度优先级睡眠状态时,它会检测自己的 PCB 表。这样进程就知道是否有给它的信号了。
当然正如上面说的当一个进程在内核态下运行时,软中断信号并不立即起作用,要等到将返回用户态时才会遍历 PCB表,才会处理信号。
补充说一下用户态和内核态:当一个进程执行系统调用进入内核代码中执行时,我们就称这个进程处在内核运行态(简称内核态),每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其所处的是用户态,当然进程也有自己的用户栈。
现在我们来分析一下上面的代码:
首先在调用 signal 系统调用时该进程处在内核态,而我们的 haha 函数则处在用户态空间,那么内核态就在用户空间为 haha 函数开辟一个临时的用户栈,首先将 signal 的返回地址进行压栈,然后在进行相应的进栈操作后执行 haha 函数,由于信号是异步处理的,所以临时开辟的用户栈空间和进程本身的用户栈空间并不冲突。
内核处理一个进程收到的软中断信号是在该进程的上下文中,因此,进程必须处于运行状态。如果进程收到一个要捕捉的信号,那么进程从内核态返回用户态时执行用户定义的函数,内核是在用户栈上创建一个新的层,该层中将返回地址的值设置成用户定义的处理函数的地址,这样进程从内核返回弹出栈顶时就返回到用户定义的函数处,从函数返回再弹出栈顶时,才返回原先进入内核的地方,接着原来的地方继续运行。这样做的原因是用户定义的处理函数不能且不允许在内核态下执行(如果用户定义的函数在内核态下运行的话,用户就可以获得任何权限)。
在信号处理方法中有几点需要特别注意:(BSD系统除外)