我有一个运行在Linux上的Python3守护进程。 这是一个正常的单线程进程在后台运行,并在主循环中执行select.select()
,然后处理I / O。 有时(一个月大约1或2次),停止响应。 当它发生时,我想debugging这个问题。
我已经尝试过pyrasite
,但是没有成功,因为守护进程的stdin / stdout被redirect到/dev/null
设备,pyrasite使用这个stdin / stdout,而不是从它开始的控制台。
所以我添加了一个SIGUSR1
信号处理程序来logging堆栈跟踪。 正常工作正常。
今天我冻结了 ps
显示,守护进程处于“S”(可中断睡眠)状态。 排除繁忙的循环。
服务器不响应SIGUSR
或SIGINT
(用于closures)。
我想至less有一些提示,那里正在发生什么。
在什么情况下,睡眠的Python3 Linux进程不处理它应该处理的中断?
更新:
我最终可以重现这个问题。 添加了很多debugging信息之后,我发现了一个即将解决的竞争条件。
当守护进程没有响应时,它在os.read(p)
处睡眠,其中p
是新pipe道的读端(参见: os.pipe
),其中没有人写入。
但是,我所有的编写简单演示程序的尝试都失败了。 当我试图从一个空pipe读取时,程序按预期被阻塞,但是可能像往常一样被中断(从SIGINT其他terminal中死亡)。 这个谜尚未解决。
UPDATE2:
最后一些代码! 我故意select了低级系统调用。
import os import time import signal import sys def sighandler(*unused): print("got signal", file=sys.stderr) print("==========") signal.signal(signal.SIGUSR1, sighandler) pid = os.getpid() rfd, wfd = os.pipe() if os.fork(): os.close(wfd) print("parent: read() start") os.read(rfd, 4096) print("parent: read() stop") else: os.close(rfd) os.kill(pid, signal.SIGUSR1) print("child: wait start") time.sleep(3) print("child: wait end")
如果你运行这么多的时间,你会得到这个:
parent: read() start got signal child: wait start child: wait end parent: read() stop
这很好,但有时你会看到这样的:
parent: read() start child: wait start child: wait end got signal parent: read() stop
这里发生了什么事情:
现在,由于我的程序中有一个错误,信号在步骤2中被接收到,但是EOF没有被传送,所以读取没有完成,步骤6(信号处理)从未到达。
这是我能够提供的所有信息。