Linux 2.4 内核学习笔记: 信号

Linux 2.4 内核学习笔记: 信号


版权申明

1.    标准信号与实时信号


0-31 这 32 个信号称为标准信号。
从 32 到 63 之间的 32 个信号称为实时信号。

可以通过 man 7 signal 查看对“标准信号”和“实时信号”详细的描述。

2.    信号排队

每个进程拥有一个信号等待队列。在 task_struct 中有一个 struct sigpending pending 域, 就是进程的信号等待队列。

当向一个进程发送信号时,信号会先被送入进程的信号等待队列,然后等到进程被调度到去处理信号的时候,会从信号等待队列中依次取出信号进行处理。

标准信号不能排队,而实时信号可以排队:
假设进程屏蔽了一个标准信号,当给它连续发送多个相同的标准信号,则只有第一个被放入进程的信号等待队列中,后续的都被丢弃。
假设进程屏蔽了一个实时信号,当给它连续发送多个相同的实时信号,则所有的信号都被放入进程的信号接收队列中。

实时信号是在 POSIX.4 实时信号扩展中定义的。

3.    信号屏蔽

进程可以屏蔽它不想接收的信号。
在 task_struct 中有一个 block 域,指定了进程要屏蔽的信号集合。

屏蔽信号带来的影响:
    1、    当向一个进程投递信号时,如果发现进程屏蔽了此信号,则即使此进程处于睡眠状态,也不唤醒它。(否则睡眠的进程会被唤醒)。
    2、    当进程开始处理它的信号等待队列的时候,对于被屏蔽的信号,不做处理。所以这些信号会一直待在等待队列中,直到进程解开对相应信号的屏蔽,才能被处理。

但是 KILL 和 TERM 这两种信号是不能屏蔽的。

4.    发送信号的处理过程

用户空间可以通过 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、    如果进程处于其它状态,则不做处理。


5.    执行信号处理函数

5.1.    进程检测信号的时机

   
1、    从系统调用、中断处理或者异常处理返回到用户空间的前夕。这和进程调度是同一个时机

在 arch/i386/kernel/entry.S 中,有如下汇编代码:

ENTRY(ret_from_sys_call)
    cli                # need_resched and signals atomic test
    cmpl $
0 ,need_resched( % ebx)
    jne reschedule
    cmpl $
0 ,sigpending( % ebx)
    jne signal_return

可以看到,在返回到用户空间前夕,会检查 task_struct 中的 sigpending 域,如果非0,说明有信号需要处理,转而去处理信号。

    2、    给进程投递完信号后,如果发现进程处于睡眠(INTERUPABLE)状态,则唤醒此进程,而进程会检查是否有信号等待处理。
    signal_wake_up()
    {
    if (t->state & TASK_INTERRUPTIBLE) {
            wake_up_process(t);
            return;
        }
    }
 

5.2.    执行信号处理函数

TBW


6.    问题   

1、    在执行信号处理程序的时候,是否要屏蔽此信号?
    在进入某个信号处理 函数前,必须暂时屏蔽掉此信号。
    当handle_signal() 执行完以后,堆栈已经被更换,接下来就会进入用户空间,执行信号处理函数。
    在handle_signal() 的最后,通过sigaddset(&current->blocked,sig); 暂时屏蔽了对此信号。

2、我们知道,当进程进入睡眠(可打断)状态,在此期间收到另一个信号,那么,进程会被唤醒,执行新的信号处理程序。
          但是,如果进程处于运行状态,且不执行任何系统调用,在此期间向它发送信号,则此进程不会立刻处理此信号。那么此进程什么时候能检查到有信号要处理了?

    答案是在时钟中断返回的时候!!!
    前面说了,检查信号的时机是从系统调用、中断或异常处理返回用户空间的前夕,因此,时钟中断返回的时候,也是检查信号的时机。
    由于时钟中断频繁发生,因此信号总会及时得到处理。

3、   在执行信号处理程序期间,虽然不会再被相同的信号打断,但仍然可能被其它信号打断。
    假设进程处理 A 信号,那么它首先需要把 A 信号从信号队列中取出,然后再去执行相应的信号处理函数,在执行过程中,如果又被另一个信号B打断,则不会再去处理 A,因为 A 已经被从信号队列中取出了。同样,必须先取下 B,然后再做处理,所以在此期间,如果又被 C 打断,则不会再处理到 B 。当 C 处理完毕后,又返回到 B 中,处理完 B,又返回到 A。

7.    对信号队列的处理

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

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值