如何在python中

时间:2017-10-15 00:42:42

标签: python linux subprocess jack process-group

我正在尝试实现一个托盘图标,让我可以控制一个进程(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"])

现在我希望我可以让我的分叉子进程和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更少魔法的模块,但仍然允许我读取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并明确地将其杀死,或者
  • 有垂死的孩子杀死杀戮

对此有何建议?

1 个答案:

答案 0 :(得分:1)

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

  1. 迭代整个子进程树,并从父对象开始全部杀死它们(这样他们就不会重启子进程)。丑陋且过于复杂的解决方案。

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

  3. 如果您仍需要中间子进程,唯一的方法是捕获发送给子进程的终止信号,并将它们发送给孙子:

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