Python多进程守护进程中的僵尸进程

7 投票
1 回答
7470 浏览
提问于 2025-04-16 20:00

在研究Python守护进程(daemon)之后,我发现这个教程看起来是最全面的:http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/

现在我正在尝试在守护进程类里面实现一个工作池,我觉得这个功能是可以的(虽然我还没有彻底测试代码),但在关闭的时候出现了僵尸进程。我了解到我需要等待子进程的返回代码,但我还不太清楚具体该怎么做。

这里有一些代码片段:

def stop(self):
    ...
    try:
        while 1:
            self.pool.close()
            self.pool.join()
            os.kill(pid, SIGTERM)
            time.sleep(0.1)
    ...

我尝试过使用os.killpg和多种os.wait的方法,但没有任何改善。我还尝试在os.kill之前和之后对工作池进行closing/joining操作。但这个循环现在是永远不会结束的,一旦执行到os.kill,就会出现僵尸进程。self.pool = Pool(processes=4)是在守护进程的__init__部分创建的。在start(self)之后执行的run(self)中,我会调用self.pool.apply_async(self.runCmd, [cmd, 10], callback=self.logOutput)。不过,我想在研究这个之前先解决僵尸进程的问题。

我该如何在守护进程中正确实现工作池,以避免出现僵尸进程呢?

1 个回答

5

在不知道子进程或守护进程内部发生了什么的情况下,我们无法对答案有100%的信心,但可以考虑一下这个问题。因为你的子进程中有工作线程,所以在收到SIGTERM信号时,你需要添加一些逻辑来处理这些线程。否则,你的进程可能不会正常退出(即使退出了,也可能不是优雅的退出)。要做到这一点,你需要:

  • 编写一个信号处理程序,用于子进程或守护进程,捕捉SIGTERM信号,并触发一个事件给主线程
  • 在子进程或守护进程的主线程中安装这个信号处理程序(这非常重要)
  • SIGTERM的事件处理程序必须向子进程或守护进程中的所有线程发出停止指令
  • 所有线程在完成后必须调用join()(如果你以为SIGTERM会自动销毁所有东西,你可能还需要实现这个逻辑)
  • 一旦所有线程都处理完毕并清理完毕,你就可以退出主线程

如果你有用于输入输出和各种其他操作的线程,这将会是一项真正的麻烦事。

另外,我通过实验发现,当使用信号处理程序时,你的事件监听器的具体策略是很重要的。例如,如果你使用select.select(),你必须设置一个超时,并在超时发生时重试;否则你的信号处理程序将不会运行。如果你有一个Queue.Queue对象用于事件,并且你的事件监听器调用它的.get()方法,你也必须使用超时,否则你的信号处理程序不会运行。(在虚拟机中用C实现的“真实”信号处理程序会运行,但你的Python信号处理程序不会,除非你使用超时。)

祝你好运!

撰写回答