2010年12月17日星期五

此daemon非彼daemon

跳槽之后被各种琐事缠绕,好久都没有写文章了,今天匆匆记一笔

最近被daemon折磨了很久。原来一个程序要成为一个合格的daemon也是很不容易的,不光要进行两次fork,还有一大堆注意事项(PEP-3143):
  • Close all open file descriptors.
  • Change current working directory.
  • Reset the file access creation mask.
  • Run in the background.
  • Disassociate from process group.
  • Ignore terminal I/O signals.
  • Disassociate from control terminal.
  • Don't reacquire a control terminal.
  • Correctly handle the following circumstances:
    • Started by System V init process.
    • Daemon termination by SIGTERM signal.
    • Children generate SIGCLD signal.

偏偏我碰到的情况更特殊一点。这段代码的作者肯定是没看过上面这些条条框框的,把自己fork了两次就变身daemon了,然后在某些时候还用multiprocessing模块把自己再复制了几份来异步的做一些事情。在调试一个bug的时候,我注意到了multiprocessing 模块里讲 Process.daemon 属性时是这么说的:
The process’s daemon flag, a Boolean value. This must be set before start() is called.

The initial value is inherited from the creating process.

When a process exits, it attempts to terminate all of its daemonic child processes.

Note that a daemonic process is not allowed to create child processes. Otherwise a daemonic process would leave its children orphaned if it gets terminated when its parent process exits. Additionally, these are not Unix daemons or services, they are normal processes that will be terminated (and not joined) if non-daemonic processes have exited.


这实在是一段很难懂的话。就像伞兵生来就是要被包围的,守护进程daemon生来就是要被父进程抛弃的,然后再被init进程收养,沉默的完成自己的任务。但这段话却明确的说,当进程退出的时候,它会把自己的子daemon进程都干掉……那daemon还守护个毛啊?而且这里还说守护进程是不能创建子进程的,这更离奇了,太不公平了吧?这是赤果果的歧视啊。

后来我发现自己有点先入为主了。daemon只是一个概念而已,而非一个实际的标签,系统中的进程实际都是一样的。multiprocess模块的 Process.daemon 只是 Process 对象的一个属性而已,是需要开发者自己设置的而非linux帮你产生啊。理解了这一点,上面的这段说明就完全可以理解了。daemonic 类型的 Process 对象就是具有这些属性而已。这里的 daemon 和我们平常理解的 daemon 虽然有联系,但确是不同的了。

有个和我有相同困惑的人09年在python-list发了一封邮件问这个问题,后来又自己理解了,我也是看到他的邮件才领悟的。

* http://www.python.org/dev/peps/pep-3143/#correct-daemon-behaviour
* http://mail.python.org/pipermail/python-list/2009-May/1203871.html