问题 父进程不会终止多进程守护程序


我有一个Python 2.7多处理过程,它不会在父进程退出时退出。我已经设置了守护进程标志,它应该强制它在父母死亡时退出。文档指出:

“当一个进程退出时,它会尝试终止所有守护进程的子进程。”

p = Process(target=_serverLaunchHelper, args=args)
p.daemon = True
print p.daemon # prints True
p.start()

当我通过kill命令终止父进程时,守护进程处于活动状态并且正在运行(在下次运行时阻塞该端口)。子进程正在启动SimpleHttpServer并调用 serve_forever 没有做任何其他事情。我的猜测是文档中的“尝试”部分意味着阻塞服务器进程正在停止进程死亡,并且它会让进程因此而变为孤立状态。我可以让孩子将服务推送到另一个线程并让主线程检查父进程id更改,但这似乎只是复制守护进程功能的很多代码。

是否有人能够深入了解守护程序标志无法正常工作的原因?这在windows8 64位和ubuntu12 32位vm上是可重复的。

流程函数的简化版本如下:

def _serverLaunchHelper(port)
    httpd = SocketServer.TCPServer(("", port), Handler)
    httpd.serve_forever()

6762
2018-04-08 20:08


起源



答案:


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

这里的关键词是“尝试”。另外,“退出”。

根据您的平台和实现,可能是终止守护进程子进程的唯一方法是明确地这样做。如果父进程正常退出,它有机会明确地这样做,所以一切都很好。但如果父进程突然终止,则不会。

特别是对于CPython,如果你看一下 来源,终止守护进程的处理方式与加入非守护进程的方式相同:通过遍历 active_children() 在一个 atexit 功能。所以,你的守护进程将被杀死,当且仅当你的守护进程被杀死 atexit 处理程序可以运行。并且,正如该模块的文档所说:

注意:当程序被Python未处理的信号杀死,检测到Python致命内部错误时,或者何时被调用时,不会调用通过此模块注册的函数 os._exit() 叫做。

根据您如何杀死父级,您可以通过添加信号处理程序来拦截突然终止来解决此问题。但你可能不会 - 例如,在POSIX上, SIGKILL 不能拦截,所以如果你 kill -9 $PARENTPID,这不是一个选择。

另一种选择是杀死进程组,而不仅仅是父进程。例如,如果您的父母有PID 12345, kill -- -12345 在Linux上将杀死它和它的所有孩子(假设你没有做任何花哨的事情)。


10
2018-04-08 20:21



好吧,所以如果我真的想保证孩子在父母去世时退出,我需要将逻辑编程到儿童过程中,以观察他们的父母消失。感谢您的回答清晰快捷! - Pyrce


答案:


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

这里的关键词是“尝试”。另外,“退出”。

根据您的平台和实现,可能是终止守护进程子进程的唯一方法是明确地这样做。如果父进程正常退出,它有机会明确地这样做,所以一切都很好。但如果父进程突然终止,则不会。

特别是对于CPython,如果你看一下 来源,终止守护进程的处理方式与加入非守护进程的方式相同:通过遍历 active_children() 在一个 atexit 功能。所以,你的守护进程将被杀死,当且仅当你的守护进程被杀死 atexit 处理程序可以运行。并且,正如该模块的文档所说:

注意:当程序被Python未处理的信号杀死,检测到Python致命内部错误时,或者何时被调用时,不会调用通过此模块注册的函数 os._exit() 叫做。

根据您如何杀死父级,您可以通过添加信号处理程序来拦截突然终止来解决此问题。但你可能不会 - 例如,在POSIX上, SIGKILL 不能拦截,所以如果你 kill -9 $PARENTPID,这不是一个选择。

另一种选择是杀死进程组,而不仅仅是父进程。例如,如果您的父母有PID 12345, kill -- -12345 在Linux上将杀死它和它的所有孩子(假设你没有做任何花哨的事情)。


10
2018-04-08 20:21



好吧,所以如果我真的想保证孩子在父母去世时退出,我需要将逻辑编程到儿童过程中,以观察他们的父母消失。感谢您的回答清晰快捷! - Pyrce