如何在 python 中杀死分叉的 child 及其 jackd 子进程
How to kill a forked child and its jackd subprocess in python
我正在尝试实现一个托盘图标,它可以让我控制一个进程 (jackd)。最值得注意的是
- 我想读取进程 stdout 和 stderr 以进行日志记录
- 我希望能够从主程序的菜单项中终止进程
杀戮让我很头疼。我相信,当我阅读 stdout 和 stderr 时,我必须在额外的 thread/process 中执行此操作以保持图标响应。所以我使用 fork() 创建了一个 child 进程并在 child 上调用了 setsid()
。然后 child 进程使用 subprocess
模块
调用 jackd
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"])
现在我希望我可以让我的分叉 child 进程和 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)
所以,杀死 child 不会杀死 grandchild,而且我看不到任何让 grandchild 的 PID (5804) 已知的干净方法到 main
以便明确地杀死它。我可能会使用一些信号技巧让垂死的 child 杀死伟大的 child,但我犹豫要不要这样做。
我也不能在 jackd 进程上使用 setpgid
来强制它进入 child 的进程组,因为它的 SID 与我的 child 的 SID 不同。
我相信我也是
- 必须说服 Popen 不要创建新会话,或者
- 使用比
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
these jack days calls setsid() to get itself established as a new
process group leader
那只剩下
的选项
- 让
main
知道 jack 的 PID 并明确杀死它,或者
- 有垂死的child kill kill jackd
有什么建议吗?
.
由于您的孙子设置了自己的会话和 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())
请注意,信号处理程序是在进程通信开始之前安装的(行读取)。
每当您的根进程将终止子进程 "gracefully"(例如,使用 SIGTERM)时,信号将被传递给孙子进程,它也会是 terminated/killed。你甚至可以扩展杀死等待和强制杀死它的逻辑。
但是,请记住,如果子进程被 SIGKILL 杀死,将没有机会捕捉到这个信号,孙进程将保持 运行 孤儿状态。因为 SIGKILL 在设计上是不可捕获的。
我正在尝试实现一个托盘图标,它可以让我控制一个进程 (jackd)。最值得注意的是
- 我想读取进程 stdout 和 stderr 以进行日志记录
- 我希望能够从主程序的菜单项中终止进程
杀戮让我很头疼。我相信,当我阅读 stdout 和 stderr 时,我必须在额外的 thread/process 中执行此操作以保持图标响应。所以我使用 fork() 创建了一个 child 进程并在 child 上调用了 setsid()
。然后 child 进程使用 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"])
现在我希望我可以让我的分叉 child 进程和 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)
所以,杀死 child 不会杀死 grandchild,而且我看不到任何让 grandchild 的 PID (5804) 已知的干净方法到 main
以便明确地杀死它。我可能会使用一些信号技巧让垂死的 child 杀死伟大的 child,但我犹豫要不要这样做。
我也不能在 jackd 进程上使用 setpgid
来强制它进入 child 的进程组,因为它的 SID 与我的 child 的 SID 不同。
我相信我也是
- 必须说服 Popen 不要创建新会话,或者
- 使用比
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
these jack days calls setsid() to get itself established as a new process group leader
那只剩下
的选项- 让
main
知道 jack 的 PID 并明确杀死它,或者 - 有垂死的child kill kill jackd
有什么建议吗?
.
由于您的孙子设置了自己的会话和 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())
请注意,信号处理程序是在进程通信开始之前安装的(行读取)。
每当您的根进程将终止子进程 "gracefully"(例如,使用 SIGTERM)时,信号将被传递给孙子进程,它也会是 terminated/killed。你甚至可以扩展杀死等待和强制杀死它的逻辑。
但是,请记住,如果子进程被 SIGKILL 杀死,将没有机会捕捉到这个信号,孙进程将保持 运行 孤儿状态。因为 SIGKILL 在设计上是不可捕获的。