2017-10-15 112 views
1

我试图实现一个托盘图标,它可以让我控制一个进程(jackd)。最值得注意的是如何杀死python中的分叉子及其jackd子进程

  • 我想读的过程输出和错误进行日志记录
  • 我希望能够从主程序菜单项杀死进程

杀伤引起我头痛。我相信,当我阅读stdout和stderr时,我必须在一个额外的线程/进程中做到这一点,以保持图标的响应。所以我用fork()来创建一个子进程,并在子进程上调用setsid()。然后,该子进程调用jackd使用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不同。

我相信我要么

  • 必须说服POPEN不是创建一个新的会话,或
  • 使用一个模块与魔法小于subprocess,但仍alows我读输出和错误。

但我不知道该怎么做。无可否认,我对过程组的了解有限。

/home/martin >python --version 
Python 2.7.1 

更新10月12日

我发现新的会话不被蟒蛇创建,而是由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()得到确立了其作为一个新的 工艺组组长

这只是离开选项

  • 使jack的PID已知main和明确杀死它,或
  • 有垂死的孩子杀杀人jackd

上有什么建议?

+0

此答案有帮助吗? http://stackoverflow.com/a/4791612 –

+0

这几乎是我在做什么。但是,我发现问题确实是自己而不是python。我会相应地更新我的问题。 –

+0

为什么不直接使用线程,直接控制'jack'?为什么你要独立制作一个子进程来控制实际的命令呢?使它变得如此复杂的假设好处是什么? –

回答

1

由于您的孙子设置了自己的会话和sid,因此您不能指望group-kill正常工作。你有三种选择:

  1. 迭代整个子进程树,并从父母开始全部杀死它们(以便它们不会重新启动孩子)。丑陋和过于复杂的解决方案。

  2. 使用主(root)进程中的线程直接控制jack子。中间儿童流程在这里可能没有必要,因为它没有任何好处。这是可能的最简单的解决方案。

  3. 如果你还必须有中间的孩子的过程中,唯一的办法是捕捉发送到子的终止信号,并传输给孙子:

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不能被设计捕获。