添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

$ ls ~yifei/notes/

父进程退出后如何退出子进程

Posted on:

Last modified:

当子进程退出的时候,父进程会收到 SIGCHLD 信号,从而可以采取相应的操作。但是当父进程退出的 时候,系统会把子进程的父进程更改为 pid=0 init 进程,却不给子进程发送任何信号。

import os
import time
def loop_print():
    import time
    while True:
        print('child alive, %s' % time.time())
        time.sleep(1)
try:
    pid = os.fork()
except OSError:
if pid != 0:  # parent
    print("parent sleep for 2")
    time.sleep(2)
    print('parent quit')
else:
    loop_print()

当父进程退出的时候,子进程一直在不断地 print, 而没有退出。

如果想在父进程退出的时候,让子进程也退出。在 Python 中可以有如下几种做法。

设置子进程为 daemon

这里的 daemon 和系统的守护进程没有任何关系,是 quit_when_parent_dies 的意思。也就是当父进程 退出的时候,会自动尝试关闭 daemon=True 的子进程。

p = multiprocessing.Process(target=foo, daemon=True)
p.start()

昨天我已经吐槽过标准库的 multiprocessing 有很多坑,不出所望,在这个问题上 multiprocessing 依然提供了半个解法,只解决了一半问题......

正常情况下,当一个程序收到 SIGTERM 或者 SIGHUP 等信号的时候,multiprocessing 会调用每个 子进程的 terminate 方法,这样会给每个子进程发送 SIGTERM 信号,子进程就可以优雅退出。然而, 当异常发生的时候,父进程挂了,比如说收到了 SIGKILL 信号,那么子进程就得不到收割,也就变成了 孤儿进程。

所以说,multiprocessing 库只解决了半个问题,真遇到问题的时候就会坑你一把。

在子进程中设置 PDEATHSIG

在 Linux 中,进程可以要求内核在父进程退出的时候给自己发信号,使用系统调用 prctl。

prctl(PR_SET_PDEATHSIG, SIGHUP);

在 Python 中也有 对应的包 python-prctl ,可以在 子进程 中这样使用,这样在父进程挂掉的时候,子进程就会收到 SIGHUP 信号:

# apt-get install build-essential libcap-dev
# pip install python-prctl
import signal
import prctl
prctl.set_pdeathsig(signal.SIGHUP)

以上面的程序为例:

import os
import time
def loop_print():
    import time
    import signal
    prctl.set_pdeathsig(signal.SIGTERM)
    while True:
        print("child alive, %s" % time.time())
        time.sleep(1)
try:
    pid = os.fork()
except OSError:
if pid != 0:  # parent
    print("parent sleep for 2")
    time.sleep(2)
    print("parent quit")
else:
    loop_print()

这次我们看到,在父进程退出的同时,子进程也退出了。

parent sleep for 2
child alive, 1539676057.5094635
child alive, 1539676058.5105338
parent quit

缺点:只支持 linux

父进程在终止的时候回收子进程

可以使用 atexit.register 在主进程中注册代码:

# pip install psutil
import psutil
import atexit
import os
import signal
@atexit.register
def kill_children():
    print("quitting, press Ctrl-C to force quit")