如何将SIGINT传递给孙进程(以及如何通过编程实现)
我在使用linux/bash/python3的时候,创建了一个孙子进程,执行的命令是sleep 123
。当我调用p.terminate()
时,这个命令会结束子进程(然后init进程接管了这个孙子进程sleep 123
)。不过,从python解释器里,我仍然可以按Ctrl+C来结束这个孙子进程。
我有两个问题:1)为什么python解释器能够给这个孙子进程发送一个SIGINT信号,即使它现在是由init进程管理的?2)我该如何通过编程的方式结束这个孙子进程。我试过p.send_signal(signal.SIGINT)
,还尝试了stdin=PIPE
等等,但就是没法成功。
gnr@localhost: python3
Python 3.3.1
>>> from subprocess import PIPE, Popen
>>> p = Popen(['bash', '-c', '(sleep 123)'])
gnr@localhost: ps -AF | grep sleep
gnr 5081 5078 0 26526 1168 6 14:03 pts/26 00:00:00 bash -c (sleep 123)
gnr 5082 5081 0 25228 564 4 14:03 pts/26 00:00:00 sleep 123
>>> p.terminate()
gnr@localhost: ps -AF | grep sleep
gnr 5082 1 0 25228 564 4 14:03 pts/26 00:00:00 sleep 123 #init inherits
>>> # hits Ctrl+C
KeyboardInterrupt
>>>
gnr@localhost: ps -AF | grep sleep
#grandchild is dead
更新:最后我使用了pexpect(这个库可以帮你处理很多pty和termios相关的事情)。
2 个回答
1
在Linux系统中,当你发送终止信号给一个进程时,这个进程的子进程并不会自动结束。它们会变成孤儿进程,然后被系统的初始化进程接管。
你可以使用psutil这个包来处理这个问题。你可以用它做一些类似下面的事情:
from subprocess import Popen
import psutil
proc = Popen(['bash', '-c', '(sleep 123)'])
parent = psutil.Process(proc.pid)
for child in parent.children(recursive=True):
child.terminate()
parent.terminate()
1
即使进行了重新父子关系的调整,孙子进程仍然把终端当作它的标准输入。所以在终端里按下Ctrl-C会给它发送一个SIGINT信号。要自己发送这个信号,你需要知道孙子进程的PID(进程ID)。不过,获取这个PID并不简单;你的子进程可能已经启动了好几个自己的子进程,而这些子进程又可能启动了更多的子进程,搞得一团乱。到底哪个才是“孙子进程”就不太清楚了。
不过,你可以给前台所有的进程发送信号。
os.kill(-os.getpgid(os.getpid()),signal.SIGINT)
当然,这样做只有在Python本身连接到终端时才有效。