2

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

因此,杀死孩子不会杀死孙子,我看不出有任何干净的方法可以让孙子的 PID(5804)知道以main明确地杀死它。我可能会使用一些信号诡计让垂死的孩子杀死孙子,但我犹豫不决。

我也不能setpgid在 jackd 进程上使用强制它进入孩子的进程组,因为它的 SID 与我孩子的 SID 不同。

我相信我要么

  • 必须说服 Popen 不要创建新会话,或者
  • 使用一个魔力低于 的模块subprocess,但它仍然允许我阅读 stdout 和 stderr。

但我也不知道该怎么做。诚然,我对进程组的了解有些有限。

/home/martin >python --version
Python 2.7.1

10 月 12 日更新

我发现新会话不是由python创建的,而是由jackd它自己创建的。当我用 替换 jackd 时xterm,一切都很好。

  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)

我也发现了这个

这些杰克时代调用 setsid() 以使自己成为新的流程组负责人

只剩下选择

  • 让 jack 的 PID 为人所知main并明确地杀死它,或者
  • 让垂死的孩子杀死 杀死杰克德

对此有何建议?

.

4

1 回答 1

1

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

  1. 遍历整个子进程树,并从父进程开始将它们全部杀死(这样它们就不会重新启动子进程)。丑陋和过于复杂的解决方案。

  2. 在主(根)进程中使用线程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 在设计上是不可捕获的。

于 2017-10-15T10:47:40.893 回答