Как убить разветвленного ребенка и его подпроцесс jackd в python

Я пытаюсь реализовать значок в трее, который позволяет мне контролировать процесс (Джек). Наиболее заметно

  • Я хочу прочитать процессы stdout и stderr для целей ведения журнала
  • Я хочу иметь возможность убить процесс из пункта меню основной программы

Убийство вызывает у меня головную боль. Я считаю, что когда я читаю 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"])

Теперь я надеялся, что смогу получить свой разветвленный дочерний процесс и подключиться к одной и той же группе процессов, чтобы я мог убить обоих сразу, но к своему удивлению 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 моего ребенка.

Я верю, что либо

  • нужно убедить Попена не создавать новую сессию, или
  • использовать модуль с меньшим количеством магии, чем subprocess, но который все еще позволяет мне читать stdout и stderr.

но я не знаю, как это сделать. Правда, мои знания о группах процессов несколько ограничены.

/home/martin >python --version
Python 2.7.1

Обновление 12 октября

Я обнаружил, что новый сеанс создан не Python, а 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(), чтобы утвердиться в качестве нового лидера группы процессов

Это оставляет только варианты

  • сделать PID Джека известным main и убить это явно, или
  • пусть умирающего ребенка убить убить джекда

Любой совет по этому поводу?

,

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 не улавливается дизайном.

Другие вопросы по тегам