在Python守护进程中使用multiprocessing模块时出错
我在使用multiprocessing模块时遇到了一个错误,这个模块是在一个python守护进程中使用的(通过python-daemon)。
Traceback (most recent call last): File "/usr/local/lib/python2.6/atexit.py", line 24, in _run_exitfuncs func(*targs, **kargs) File "/usr/local/lib/python2.6/multiprocessing/util.py", line 262, in _exit_function for p in active_children(): File "/usr/local/lib/python2.6/multiprocessing/process.py", line 43, in active_children _cleanup() File "/usr/local/lib/python2.6/multiprocessing/process.py", line 53, in _cleanup if p._popen.poll() is not None: File "/usr/local/lib/python2.6/multiprocessing/forking.py", line 106, in poll pid, sts = os.waitpid(self.pid, flag) OSError: [Errno 10] No child processes
这个守护进程(父进程)会创建几个子进程,然后定期检查这些子进程是否完成。如果父进程发现某个子进程已经完成,它就会尝试重启这个子进程。就在这个时候,上面的错误就出现了。看起来一旦某个子进程完成,任何与multiprocessing模块相关的操作都会引发这个错误。如果我在一个非守护的python脚本中运行相同的代码,就不会出现任何错误。
补充:
示例脚本
from daemon import runner
class DaemonApp(object):
def __init__(self, pidfile_path, run):
self.pidfile_path = pidfile_path
self.run = run
self.stdin_path = '/dev/null'
self.stdout_path = '/dev/tty'
self.stderr_path = '/dev/tty'
def run():
import multiprocessing as processing
import time
import os
import sys
import signal
def func():
print 'pid: ', os.getpid()
for i in range(5):
print i
time.sleep(1)
process = processing.Process(target=func)
process.start()
while True:
print 'checking process'
if not process.is_alive():
print 'process dead'
process = processing.Process(target=func)
process.start()
time.sleep(1)
# uncomment to run as daemon
app = DaemonApp('/root/bugtest.pid', run)
daemon_runner = runner.DaemonRunner(app)
daemon_runner.do_action()
#uncomment to run as regular script
#run()
6 个回答
0
我记得之前在主干和2.6的维护版本里做了一些修复,这应该能帮到你。你能试着在python-trunk或者最新的2.6-maint svn里运行你的脚本吗?我这边没法查看到错误信息。
7
你的问题是守护进程和多进程模块之间的冲突,特别是在处理SIGCLD(子进程终止)信号时。守护进程在启动时将SIGCLD设置为SIG_IGN,这样在Linux系统上,终止的子进程会立即被回收,而不是变成僵尸进程,直到父进程调用wait()。但是,多进程模块的is_alive测试会调用wait()来检查进程是否还活着,如果进程已经被回收,这个检查就会失败。
最简单的解决办法就是把SIGCLD重新设置为SIG_DFL(默认行为——忽略这个信号,让父进程通过wait()来等待终止的子进程):
def run():
# ...
signal.signal(signal.SIGCLD, signal.SIG_DFL)
process = processing.Process(target=func)
process.start()
while True:
# ...
5
忽略 SIGCLD
也会导致 subprocess
模块出现问题,这是因为这个模块有一个bug(详细信息可以查看issue 1731717,这个问题在2011年9月21日时仍然没有解决)。
在 python-daemon
库的 1.4.8版本 中,解决了这个问题;现在它不再默认处理 SIGCLD
,因此不再与其他标准库模块产生这种不愉快的冲突。