21

使用python-daemon时,我正在创建子进程,如下所示:

import multiprocessing

class Worker(multiprocessing.Process):
   def __init__(self, queue):
      self.queue = queue # we wait for things from this in Worker.run()

   ...

q = multiprocessing.Queue()

with daemon.DaemonContext():
    for i in xrange(3):
       Worker(q)

    while True: # let the Workers do their thing
       q.put(_something_we_wait_for())

当我用 Ctrl-C 或 SIGTERM 等杀死父守护进程(即不是 Worker)时,孩子不会死。一个人如何杀死孩子?

我的第一个想法是使用atexit杀死所有的工人,就像这样:

 with daemon.DaemonContext():
    workers = list()
    for i in xrange(3):
       workers.append(Worker(q))

    @atexit.register
    def kill_the_children():
        for w in workers:
            w.terminate()

    while True: # let the Workers do their thing
       q.put(_something_we_wait_for())

然而,守护进程的子进程处理起来很棘手,我有义务就如何完成这件事提出想法和意见。

谢谢你。

4

3 回答 3

32

你的选择有点有限。如果self.daemon = True在类的构造函数中执行Worker操作不能解决您的问题并且尝试在 Parent(即SIGTERM, SIGINT)中捕获信号不起作用,您可能必须尝试相反的解决方案 - 而不是让父母杀死孩子,您可以让孩子在父母去世时自杀。

第一步是将构造函数提供给WorkerPID进程(您可以使用 来执行此操作os.getpid())。然后,不只是self.queue.get()在工作循环中做,而是做这样的事情:

waiting = True
while waiting:
    # see if Parent is at home
    if os.getppid() != self.parentPID:
        # woe is me! My Parent has died!
        sys.exit() # or whatever you want to do to quit the Worker process
    try:
        # I picked the timeout randomly; use what works
        data = self.queue.get(block=False, timeout=0.1)
        waiting = False
    except queue.Queue.Empty:
        continue # try again
# now do stuff with data

上面的解决方案检查父 PID 是否与原来的不同(即子进程是否被父进程采用initlauchd因为父进程死亡) - 请参阅参考资料。但是,如果由于某种原因这不起作用,您可以将其替换为以下函数(改编自此处):

def parentIsAlive(self):
    try:
        # try to call Parent
        os.kill(self.parentPID, 0)
    except OSError:
        # *beeep* oh no! The phone's disconnected!
        return False
    else:
        # *ring* Hi mom!
        return True

现在,当父母去世(无论出于何种原因)时,子工人会像苍蝇一样自发地掉下来——正如你所愿,你这个守护进程!:-D

于 2010-04-09T21:04:08.627 回答
4

您应该在第一次创建子项时存储父 pid(比如说 in self.myppid),并且何时self.myppid不同于getppid()意味着父进程死亡。

为避免检查父级是否一次又一次地更改,您可以使用信号文档PR_SET_PDEATHSIG中描述的方法。

5.8 Linux“父母死亡”信号

每个进程都有一个变量 pdeath_signal,在 fork() 或 clone() 之后初始化为 0。它给出了进程在其父进程死亡时应该得到的信号。

在这种情况下,您希望您的进程终止,您可以将其设置为 a SIGHUP,如下所示:

prctl(PR_SET_PDEATHSIG, SIGHUP);
于 2010-04-09T21:23:02.377 回答
2

Atexit 无法解决问题——它只会在成功的非信号终止时运行——请参阅文档顶部附近的注释。您需要通过两种方式之一设置信号处理。

听起来更简单的选项:根据http://docs.python.org/library/multiprocessing.html#process-and-exceptions在您的工作进程上设置守护进程标志

听起来有点难的选项:PEP-3143似乎暗示有一种内置方法可以在 python-daemon 中挂钩程序清理需求。

于 2010-04-07T16:44:18.823 回答