shell /内核如何处理发送到同一进程组内进程的信号?

时间:2016-09-26 12:57:55

标签: linux linux-kernel signals internals

在UNIX环境中高级编程的第9.6节中,我们可以阅读:

  

每当我们按下终端的中断键(通常是DELETE或Control-C)时,中断信号就会被发送到前台进程组中的所有进程。

我通过编写超级简单的Python脚本,处理sigint信号和比较结果进行了简单的测试:

#!/usr/bin/python3

import os
import sys
import signal

def handler(signum, frame):
    p = os.getpid()
    with open(str(p), 'w+') as fh:
        fh.write("Received signal at pid: {0}".format(os.getpid()))
    raise SystemError()


def main():
    signal.signal(signal.SIGINT, handler)
    for line in sys.stdin:
        print("READ {0}".format(line.rstrip('\n')))

main()

我只是按照以下方式运行它:

$ ./reader.py | ./reader.py | ./reader.py

并确保流程属于同一流程组:

$ ps -ae pid,ppid,pgid,sess,comm
$ PID  PPID  PGID  SESS COMMAND
  9702  5930  9702  5930 reader.py
  9703  5930  9702  5930 reader.py
  9704  5930  9702  5930 reader.py

我的理解是,在向任何进程发送INT信号后,每个进程都应该接收信号并使用处理程序处理它。 可悲的是,事实并非如此:

  1. 如果我将信号发送给流程组负责人(在这种情况下为pid 9702),所有流程都将终止,但我只能看到流程组负责人调用信号处理函数(只创建了一个文件)处理程序调用执行路径)

  2. 如果我将信号发送到不是进程组负责人的进程,则进程将退出,处理信号,进程后创建的同一管道(进程组)也是"默默地"终止。

     $ ps -ao pid,ppid,pgid,sess,comm
     PID  PPID  PGID  SESS COMMAND
     9842  5930  9842  5930 reader.py
     9843  5930  9842  5930 reader.py
     9844  5930  9842  5930 reader.py
    
     $kill -INT 9843
    
     $ ps -ao pid,ppid,pgid,sess,comm
     PID  PPID  PGID  SESS COMMAND
     1456  1446  1456  1446 weechat
     5893  5873  5893  5873 screen
     5928  5902  5928  5902 screen
     9842  5930  9842  5930 reader.py
    
  3. 我想理解为什么这里的行为与书中描述的内容不一致(属于同一进程组的所有进程在向其中任何一个发送信号时终止)。特别是如何将信号传递给多个进程,而不是所有信号处理程序都被调用。谢谢你的解释。

1 个答案:

答案 0 :(得分:1)

看起来我想通了。我的理解是CTRL-C只是将SIGINT信号发送给流程组织领导者,但看起来这样更复杂,shell将负责向进程组中的每个进程发送信号。

在我的测试中,为什么只调用一个信号处理程序的原因是其他进程正确退出。在调用 proc1 |之后proc2 | proc3 和kill,比如proc1,它处理信号,proc2和proc在输入关闭时退出。在for循环之后添加测试sleeps()表明如果我杀死proc1剩余的进程仍然存活。