如何在python中杀死一个分叉的孩子及其jackd子进程
Posted
技术标签:
【中文标题】如何在python中杀死一个分叉的孩子及其jackd子进程【英文标题】:How to kill a forked child and its jackd subprocess in python 【发布时间】:2018-03-26 19:32:23 【问题描述】:我正在尝试实现一个托盘图标,它可以让我控制一个进程(jackd)。最值得注意的是
我想读取进程 stdout 和 stderr 以进行日志记录 我希望能够从主程序的菜单项中终止进程杀戮让我头疼。我相信,当我阅读 stdout 和 stderr 时,我必须在额外的线程/进程中执行此操作以保持图标响应。所以我使用 fork() 创建了一个子进程并在子进程上调用了setsid()
。然后该子进程使用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"])
现在我希望我可以让我的分叉子进程进入同一个进程组,这样我就可以一次杀死两者,但令我惊讶的是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 不同。
我相信我也是
必须说服 Popen 不要创建新会话,或者 使用一个比subprocess
魔法更少的模块,但它仍然允许我读取标准输出和标准错误。
但我也不知道该怎么做。诚然,我对进程组的了解有些有限。
/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
知道 jack 的 PID 并明确杀死它,或者
让垂死的孩子杀死杰克德
对此有何建议?
.
【问题讨论】:
这个答案有帮助吗? ***.com/a/4791612 这就是我正在做的事情。但是,我发现问题实际上是jackd本身而不是python。我会相应地更新我的问题。 为什么不只用一个线程,直接控制jack
呢?为什么要创建一个单独的子进程来控制实际命令?让它变得如此复杂有什么好处?
fork child 用于捕获jackd的输出。我不认为我可以在不阻塞的情况下在***流程中做到这一点。
你应该可以,只要不在主线程中。主线程应该继续做它的 UI 工作,包括。响应来自操作系统的检查。这是此类应用程序中的常见模式。
【参考方案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 在设计上是无法捕获的。
【讨论】:
以上是关于如何在python中杀死一个分叉的孩子及其jackd子进程的主要内容,如果未能解决你的问题,请参考以下文章