进程在等待另一个进程终止时的信号处理

时间:2013-08-22 20:07:56

标签: c++ c linux process signals

我只是试图从运行过程的内核和用户模式中分别理解信号处理的概念。

           PROCESS-1       --------------------> PROCESS-3
        (parent process)  <-------------------
             ^               process-3 sending signals(SIGPIPE-for communication) or
             ||              SIGSTOP or SIGKILL to process-1
             ||
             ||
             ||process-1 waiting for child process-2
             || using waitpid command.
             ||
              v
           PROCESS-2(waiting for resource, page fault happened, etc)
        (child process)

我想知道内核如何将进程-3的信号发送到进程-1,知道进程-1正在等待进程2完成。希望在信号处理场景(PCB,资源,打开文件描述符等)中了解有关用户和内核通信的更多信息。请解释与此相关的内容..

任何给予的帮助都很感激.. !!!

1 个答案:

答案 0 :(得分:1)

内核并不关心进程-1是否“等待进程2完成”(特别是它对“为什么”处于状态不感兴趣,只是它在某种状态下:在这种情况下,在内核中空闲等待某个事件)。对于典型的 1 捕获信号,信号发送器实质上只是在信号接收器的进程/线程状态中设置一些位,然后在适当的情况下,调度进程/线程运行以便它可以看到那些位。如果接收器在内核中空闲等待某个事件,那就是“运行计划”案例之一。 (其他常见情况包括:接收器处于STOP状态,除了SIGCONT信号外,它处于停止状态;或者,接收器在用户模式下运行,设置为转换到内核模式以便注意待处理的信号。)

无法捕获或忽略SIGKILLSIGSTOP,因此,不,您无法为这些提供处理程序。 (通常情况下,流程会通过SIGTSTPSIGTTINSIGTTOU进入停止状态,所有都可以被捕获或忽略。)

如果在用户信号处理程序返回后(通过SA_RESTART的{​​{1}}标志)将系统调用设置为重新启动,则可以通过设置{{1}的“返回地址”来实现实际上,操作使系统再次调用。也就是说,如果process-1在sigaction()中,则从初始sigreturn()点开始,通过接收捕获信号waitpid()的操作序列(从进程-1的角度来看) ,并回到更多等待,是:

  1. 系统调用:waitpid()
  2. 让自己入睡等待活动
  3. 觉醒:检查觉醒事件
  4. 事件是信号和信号被捕获,所以:
  5. 根据s设置设置新的信号掩码(请参阅waitpid()
  6. 在堆栈上推送信号帧(请参阅sigaction()sigaction()
  7. 设置用户代码(程序计数器)以输入“signal trampoline”
  8. 返回用户代码(进入蹦床)
  9. (此时,进程-1重新进入用户模式。其余步骤未编号,因为我无法在9开始启动。:-))

    • 调用用户处理程序例程(仍在上面选择的堆栈上)
    • 当用户例程返回时,执行SA_ONSTACK系统调用,     使用在设置时存储的帧,可能已修改     按用户例程

    (此时进程进入内核模式,执行sigaltstack()系统调用)

    • 系统调用:sigreturn():设置sigreturn()参数
    • 指定的信号掩码
    • 设置其他寄存器,包括堆栈指针和     程序计数器,由sigreturn()参数
    • 指定
    • 返回用户代码

    (程序现在回到用户模式,寄存器设置为输入sigreturn()

    1. 系统调用:sigreturn()
    2. 此时,进程返回到收到捕获信号之前的状态:waitpid使其等待事件进入休眠状态(步骤2)。一旦被唤醒(步骤3),它正在等待的事件已经发生(例如,进程为waitpid() - 已完成)并且它可以正常返回,或者已经发生了另一个捕获的信号并且它应该重复该序列,或者它被杀死了它应该清理,或者其他什么。

      这个序列就是为什么一些系统调用(比如一些waitpid - 就像系统调用一样)会在信号中断的情况下“早退”:他们在“第一次”进入内核之间做了一些不可逆转的事情和信号处理程序的运行时间。在这种情况下,在步骤6 推送的信号帧不得具有程序计数器值,该值导致整个系统调用重新启动。如果确实如此,那么在该过程进入睡眠状态之前完成的不可逆转的工作将会丢失。因此,它被设置为返回检测成功系统调用的指令,寄存器值设置为返回短waitpid()计数,或者其他。

      当系统调用设置为而非重启(read()未设置)时,步骤6中推送的信号帧也不同。它返回到检测系统调用失败的指令,而不是返回执行系统调用的指令,寄存器值设置为表示read()错误。

      (通常,但并非总是如此,这些是相同的指令,例如,测试成功/失败的条件分支。在我原来的SPARC端口中,我在大多数情况下使它们成为不同的指令。因为叶子例程返回到{{没有寄存器或堆栈操作,我只是设置一个位,指示成功返回应该返回叶子例程的返回地址。所以大多数系统调用只是“将系统调用号和成功重新标记放入{{1然后陷阱到内核,然后跳转到错误处理,因为如果我们到达这里,系统调用必定已经失败。“)


      1 queued signals对比。