关于Linux信号的总结

中断是硬件与操作系统之间的一种通信方式;信号是进程间(内核进程+用户进程)的一种通信方式。信号的实现类似中断,所以很多人都称其为软中断。

标准信号与实时信号

0-31 这 32 个信号称为标准信号。从 32 到 63 之间的 32 个信号称为实时信号。可以通过 man 7 signal 查看对“标准信号”和“实时信号”详细的描述。

信号排队

每个进程拥有一个信号等待队列。在 task_struct 中有一个 struct sigpending pending 域, 就是进程的信号等待队列。当向一个进程发送信号时,信号会先被送入进程的信号等待队列,然后等到进程被调度到去处理信号的时候,会从信号等待队列中依次取出信号进行处理。标准信号不能排队,而实时信号可以排队:假设进程屏蔽了一个标准信号,当给它连续发送多个相同的标准信号,则只有第一个被放入进程的信号等待队列中,后续的都被丢弃。假设进程屏蔽了一个实时信号,当给它连续发送多个相同的实时信号,则所有的信号都被放入进程的信号接收队列中。实时信号是在 POSIX.4 实时信号扩展中定义的。

信号屏蔽

进程可以屏蔽它不想接收的信号。在 task_struct 中有一个 block 域,指定了进程要屏蔽的信号集合。屏蔽信号带来的影响: 1、 当向一个进程投递信号时,如果发现进程屏蔽了此信号,则即使此进程处于睡眠状态,也不唤醒它。(否则睡眠的进程会被唤醒)。 2、 当进程开始处理它的信号等待队列的时候,对于被屏蔽的信号,不做处理。所以这些信号会一直待在等待队列中,直到进程解开对相应信号的屏蔽,才能被处理。(按照以上理解,也就是虽说某些信号被屏蔽,但是这个信号会被保存在pending里,只是在进程从内核返回处理信号时不处理被阻塞(屏蔽)的信号,直到该信号被解除屏蔽)但是 KILL 和 TERM 这两种信号是不能屏蔽的。

发送信号的处理过程

用户空间可以通过 kill() 或 sigqueue() 两个系统调用来向一个进程发送信号。内核空间的入口是 sys_kill():sys_kill() ==> kill_something_info() ==> kill_proc_info() ==> send_sig_info() ==> deliver_signal() ==> send_signal() 1、 首先,根据 PID 找到对应的目标进程。 这是通过 find_task_by_pid() 实现的。 2、 如果目标进程对信号的处理行为是“忽略”,则无需投递 3、 对于标准信号,如果前面已经有一个相同信号到达,进程还没来得及处理,则不再投递,信号丢失。 4、 否则,信号被挂在进程的信号等待队列中。 5、 如果进程屏蔽了此信号,则不唤醒此进程 6、 否则,如果此进程处于 INTERUPABLE 状态,则唤醒此进程。 signal_wake_up() ==> wake_up_process() 7、 如果进程处于其它状态,则不做处理。

执行信号处理函数 进程检测信号的时机

1、 从系统调用、中断处理或者异常处理返回到用户空间的前夕。这和进程调度是同一个时机在 arch/i386/kernel/entry.S 中,有如下汇编代码:

ENTRY(ret_from_sys_call)cli#need_reschedandsignalsatomictestcmpl$0,need_resched(%ebx)jnereschedulecmpl$0,sigpending(%ebx)jnesignal_return

可以看到,在返回到用户空间前夕,会检查 task_struct 中的 sigpending 域,如果非0,说明有信号需要处理,转而去处理信号。 2、 给进程投递完信号后,如果发现进程处于睡眠(INTERUPABLE)状态,则唤醒此进程,而进程会检查是否有信号等待处理。 signal_wake_up() { if (t->state & TASK_INTERRUPTIBLE) { wake_up_process(t); return; } }

问题

1、 在执行信号处理程序的时候,是否要屏蔽此信号? 在进入某个信号处理 函数前,必须暂时屏蔽掉此信号。 当handle_signal() 执行完以后,堆栈已经被更换,接下来就会进入用户空间,执行信号处理函数。 在handle_signal() 的最后,通过sigaddset(&current->blocked,sig); 暂时屏蔽了对此信号。2、我们知道,当进程进入睡眠(可打断)状态,在此期间收到另一个信号,那么,进程会被唤醒,执行新的信号处理程序。 但是,如果进程处于运行状态,且不执行任何系统调用,在此期间向它发送信号,则此进程不会立刻处理此信号。那么此进程什么时候能检查到有信号要处理了? 答案是在时钟中断返回的时候!!! 前面说了,检查信号的时机是从系统调用、中断或异常处理返回用户空间的前夕,因此,时钟中断返回的时候,也是检查信号的时机。 由于时钟中断频繁发生,因此信号总会及时得到处理。3、 在执行信号处理程序期间,虽然不会再被相同的信号打断,但仍然可能被其它信号打断。 假设进程处理 A 信号,那么它首先需要把 A 信号从信号队列中取出,然后再去执行相应的信号处理函数,在执行过程中,如果又被另一个信号B打断,则不会再去处理 A,因为 A 已经被从信号队列中取出了。同样,必须先取下 B,然后再做处理,所以在此期间,如果又被 C 打断,则不会再处理到 B 。当 C 处理完毕后,又返回到 B 中,处理完 B,又返回到 A。

对信号队列的处理

假设有多个信号在队列上等待。在一次调度时机中,进程检查它的信号等待队列,对于处理方式为 SIG_IGN 和 SIG_DFL 的信号,不用返回到用户空间。对于需要返回到用户空间去执行信号处理函数的情况,在一次调度时机中只处理一个。

本文转自:http://blog.csdn.net/rstevens/archive/2007/09/27/1803801.aspx

大把大把的时光从指缝间遛走,

关于Linux信号的总结

相关文章:

你感兴趣的文章:

标签云: