python - 从bash调用脚本时终止子进程

时间:2014-02-28 00:02:05

标签: python linux bash process multiprocessing

我有一个python脚本:zombie.py

from multiprocessing import Process
from time import sleep
import atexit

def foo():
    while True:
        sleep(10)

@atexit.register
def stop_foo():
    p.terminate()
    p.join()

if __name__ == '__main__':
    p = Process(target=foo)
    p.start()

    while True:
        sleep(10)

当我使用python zombie.py &运行此操作并使用kill -2终止父进程时,正确调用stop()并且两个进程都终止。

现在,假设我有一个bash脚本zombie.sh:

#!/bin/sh

python zombie.py &

echo "done"

我从命令行运行./zombie.sh

现在,当父母被杀时,stop()永远不会被调用。如果我在父进程上运行kill -2,则没有任何反应。 kill -15kill -9只会杀死父进程,但不会杀死子进程:

[foo@bar ~]$ ./zombie.sh 
done
[foo@bar ~]$ ps -ef | grep zombie | grep -v grep
foo 27220     1  0 17:57 pts/3    00:00:00 python zombie.py
foo 27221 27220  0 17:57 pts/3    00:00:00 python zombie.py
[foo@bar ~]$ kill -2 27220
[foo@bar ~]$ ps -ef | grep zombie | grep -v grep
foo 27220     1  0 17:57 pts/3    00:00:00 python zombie.py
foo 27221 27220  0 17:57 pts/3    00:00:00 python zombie.py
[foo@bar ~]$ kill 27220
[foo@bar ~]$ ps -ef | grep zombie | grep -v grep
foo 27221     1  0 17:57 pts/3    00:00:00 python zombie.py

这里发生了什么?如何确保子进程与父进程一起消失?

2 个答案:

答案 0 :(得分:1)

更新:此解决方案不适用于被信号杀死的进程。


您的子进程不是zombie。它还活着。

如果您希望子进程在其父正常退出时被终止,请在p.daemon = True之前设置p.start()。来自the docs

  

当进程退出时,它会尝试终止所有守护进程子进程。

查看the source code,很明显multiprocessing使用atexit回调来杀死其守护孩子,即如果父母被信号杀死,它将无效。例如:

#!/usr/bin/env python
import logging
import os
import signal
import sys
from multiprocessing import Process, log_to_stderr
from threading import Timer
from time import sleep

def foo():
    while True:
        sleep(1)

if __name__ == '__main__':
    log_to_stderr().setLevel(logging.DEBUG)
    p = Process(target=foo)
    p.daemon = True
    p.start()

    # either kill itself or exit normally in 5 seconds
    if '--kill' in sys.argv:
        Timer(5, os.kill, [os.getpid(), signal.SIGTERM]).start()
    else: # exit normally
        sleep(5)

输出

$ python kill-orphan.py
[INFO/Process-1] child process calling self.run()
[INFO/MainProcess] process shutting down
[DEBUG/MainProcess] running all "atexit" finalizers with priority >= 0
[INFO/MainProcess] calling terminate() for daemon Process-1
[INFO/MainProcess] calling join() for process Process-1
[DEBUG/MainProcess] running the remaining "atexit" finalizers

注意“为守护进程”行调用terminate()。

输出(--kill

$ python kill-orphan.py --kill
[INFO/Process-1] child process calling self.run()

日志显示如果父项被信号杀死,则不会调用“atexit”回调(并且ps表示在这种情况下孩子还活着)。另请参阅Multiprocess Daemon Not Terminating on Parent Exit

答案 1 :(得分:1)

atexitp.daemon = True都不会真正确保子进程将与父亲一起死亡。接收SIGTERM不会触发atexit例程。

为了确保孩子在父亲去世后被杀,你必须在父亲身上安装信号处理程序。通过这种方式,您可以对大多数信号(SIGQUIT,SIGINT,SIGHUP,SIGTERM,......)做出反应,但不能对SIGKILL做出反应;根本没有办法对接收它的过程中的信号做出反应。

为所有有用信号安装信号处理程序,并在该处理程序中终止子进程。