我有一个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 -15
或kill -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
这里发生了什么?如何确保子进程与父进程一起消失?
答案 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)
atexit
和p.daemon = True
都不会真正确保子进程将与父亲一起死亡。接收SIGTERM不会触发atexit
例程。
为了确保孩子在父亲去世后被杀,你必须在父亲身上安装信号处理程序。通过这种方式,您可以对大多数信号(SIGQUIT,SIGINT,SIGHUP,SIGTERM,......)做出反应,但不能对SIGKILL做出反应;根本没有办法对接收它的过程中的信号做出反应。
为所有有用信号安装信号处理程序,并在该处理程序中终止子进程。