我正在尝试实现一个托盘图标,让我可以控制一个进程(jackd)。最值得注意的是
杀戮让我头疼。我相信,当我读取stdout和stderr时,我必须在额外的线程/进程中执行此操作以保持图标响应。所以我使用fork()来创建子进程并在子进程上调用setsid()
。然后,该子进程使用subprocess
模块
from subprocess import PIPE, STDOUT, Popen
def runAndLog(self, cmd):
po = Popen(cmd, stdout=PIPE, stderr=STDOUT)
line = po.stdout.readline()
while line:
print(line.rstrip())
line = po.stdout.readline()
self.runAndLog(["jackd","-R", "-P80", "-t5000", "-dfirewire", "-r44100", "-p256", "-n3"])
现在我希望我可以让我的分叉子进程和jackd进入同一个进程组,所以我可以一次性杀死两个,但令我惊讶Popen
创建了一个新的进程组和一个新的会话:
PID PPID PGID SID COMMAND
5790 5788 5788 31258 python (parent, main)
5791 5790 5791 5791 python (forked child after setsid - ok)
5804 5791 5804 5804 jackd (grandchild created by Popen)
所以,杀死孩子不会杀死孙子,我也没有看到任何干净的方法让main
知道孙子的PID(5804)杀死明确地说。我可能会使用一些信号技巧让垂死的孩子杀死孙子,但我犹豫不决。
我也无法在jackd进程中使用setpgid
强制它进入子进程组,因为它的SID与我孩子的SID不同。
我相信我要么
subprocess
更少魔法的模块,但仍然允许我读取stdout和stderr。但我不知道该怎么做。不可否认,我对过程组的了解有限。
/home/martin >python --version
Python 2.7.1
10月12日更新
我发现新会话不是由python创建的,而是由jackd
本身创建的。当我用xterm
替换jackd时,一切都很好。
PID PPID PGID SID COMMAND
12239 4024 12239 4024 python (parent, main)
12244 12239 12244 12244 python (forked child after setsid - ok)
12249 12244 12244 12244 xterm (grandchild has same PGID and SID - ok)
我还找到了this
这些杰克日呼吁setsid()将自己建立为新的 流程组负责人
只留下选项
main
知道杰克的PID并明确地将其杀死,或者对此有何建议?
答案 0 :(得分:1)
由于你的孙子设置了自己的会话和sid,你不能指望group-kill能够正常工作。您有三种选择:
迭代整个子进程树,并从父对象开始全部杀死它们(这样他们就不会重启子进程)。丑陋且过于复杂的解决方案。
使用主(根)进程中的线程直接控制jack
子进程。这里可能没有必要使用中间子进程,因为它没有任何好处。这是最简单的解决方案。
如果您仍需要中间子进程,唯一的方法是捕获发送给子进程的终止信号,并将它们发送给孙子:
child.py
:
import signal
import subprocess
# The signal handler for the child process, which passes it further to the grandchild.
def handler(signum, frame):
print('Killing self, killing the children')
os.kill(proc.pid, signal.SIGTERM)
# wait for the process to die gracefully for N secs.
time.sleep(5)
os.kill(proc.pid, signal.SIGKILL)
cmd = ["jackd","-R", "-P80", "-t5000", "-dfirewire", "-r44100", "-p256", "-n3"]
po = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
signal.signal(signal.SIGTERM, handler)
for line in po.stdout:
print(line.rstrip())
请注意,信号处理程序在进程通信开始之前安装(行读取)。
每当你的根进程将杀死子进程"优雅" (例如,使用SIGTERM),信号将被传递给孙子,它也将被终止/杀死。你甚至可以扩展杀戮和等待的逻辑并强制杀死它。
但是,请记住,如果使用SIGKILL杀死子进程,则无法捕获此信号,并且孙子将继续运行孤儿。因为SIGKILL不能被设计捕获。