как убить группу процессов с помощью подпроцесса Python

Я пытаюсь сделать эквивалент следующего, используя подпроцесс Python:

>cat /var/log/dmesg | festival --tts &
[1] 30875
>kill -9 -30875

Обратите внимание, что я убиваю процесс group (на что указывает отрицательный знак перед идентификатором процесса), чтобы убить все дочерние процессы, запускаемые Festival.

В Python у меня в настоящее время есть следующий код, в котором два процесса создаются и связываются через канал.

process_cat = subprocess.Popen([
    "cat",
    "/var/log/dmesg"
], stdout = subprocess.PIPE)
process_Festival = subprocess.Popen([
    "festival",
    "--tts"
], stdin = process_cat.stdout, stdout = subprocess.PIPE)

Как мне убить эти процессы и их дочерние процессы способом, эквивалентным способу Bash, показанному выше? Следующего подхода недостаточно, потому что он не убивает дочерние процессы:

os.kill(process_cat.pid, signal.SIGKILL)
os.kill(process_Festival.pid, signal.SIGKILL)

Есть ли более элегантный способ сделать это, возможно, используя всего один процесс?


person d3pd    schedule 26.08.2015    source источник
comment
Почему этого недостаточно? Может взглянуть на os.setprgid   -  person betterworld    schedule 26.08.2015
comment
Этого недостаточно, потому что он не убивает дочерние процессы Festival. Отредактирую вопрос для ясности.   -  person d3pd    schedule 26.08.2015


Ответы (1)


Вы можете значительно упростить это, поскольку вам редко требуется cat |. Например:

process_Festival = subprocess.Popen(["festival", "--tts", "/var/log/dmesg"])

тогда позже

process_Festival.send_signal(1)

Если вы завершите фестиваль с помощью такого сигнала, как SIGHUP, а не SIGKILL, он правильно очистит все подпроцессы.


Там очень хорошее объяснение как создать новую группу процессов с подпроцессом python. . Добавление опции preexec_fn=os.setsid в Popen:

process_Festival = subprocess.Popen(["festival", "--tts", "/var/log/dmesg"],preexec_fn=os.setsid)

Затем вы можете получить группу процессов из идентификатора процесса и сигнализировать ей:

pgrp = os.getpgid(process_Festival.pid)
os.killpg(pgrp, signal.SIGINT)

Примечание: начиная с Python 3.2 вы также можете использовать start_new_session=True в вызове Popen вместо preexec_fn.

person meuh    schedule 26.08.2015
comment
Всем привет. Спасибо за предложение по упрощению. В этом случае вы совершенно правы (хотя я хочу использовать каналы для других, более сложных целей). Что касается вашего подхода send_signal(9), проблема в том, что он эквивалентен чему-то вроде kill -9 30875, а не kill -9 -30875. Ваш подход не убивает множество дочерних процессов, созданных Festival, что мне и нужно. - person d3pd; 26.08.2015
comment
есть os.killpg(pg,signal), а process_Festival.pid - это идентификатор процесса. - person meuh; 26.08.2015
comment
не используйте kill -9, но -1, чтобы фестиваль мог очистить своих детей. - person meuh; 26.08.2015
comment
Большое спасибо за ваши предложения. Мне нужно убить процесс group, а не заставить Festival завершить дочерние процессы, потому что дочерние процессы могут продолжать работать в течение очень долгого времени, прежде чем они будут завершены, что не подходит для моих целей. Вы знаете, как использовать функцию os.killpg с подпроцессом? Не похоже, что его можно использовать с PID, возвращаемым одним процессом подпроцесса. - person d3pd; 26.08.2015
comment
есть os.getpgid(pid), чтобы получить группу процессов из pid. - person meuh; 26.08.2015
comment
Хм, наверное, это глупый вопрос, но как мне заставить os.getpgid не убивать скрипт Python, который его запускает? Кажется, что все работает, когда я использую аргумент preexec_fn = os.setsid при создании процесса, но я его не понимаю. Еще раз спасибо за все ваши идеи по этому поводу. - person d3pd; 26.08.2015
comment
Если вы упомянули об этом в своем решении, я с радостью его приму. - person d3pd; 26.08.2015
comment
Я нашел хорошую статью о разнице между сессиями и группами процессов. andy-pearce.com/blog/posts / 2013 / август / process-groups-and-sessions. Итак, если вы хотите запустить процесс-демон, вам нужно использовать параметр os.setsid или start_new_session. Но если вы создаете только дочерние процессы, которые должны умереть в момент выхода из терминала, предпочтительнее использовать preexec_fn=os.setpgrp - person Konstantin Nikitin; 11.04.2019