我使用 Python 2.7 中的 asynchat 和邮箱模块编写了一个电子邮件服务器。在前台运行时,一切正常,并且在长时间的测试中保持稳定。但是,当作为守护进程分离时,对邮箱的访问会导致 asyncore.loop() 中出现错误文件描述符的异常。只有文件访问是一个问题。Socket I/O 继续正常工作。
__init__()
在 found_terminator() 函数中或以后打开文件似乎并不重要。
我正在使用 Schroeder 的 ActiveState 配方 278731 中的代码分离进程,但使用单独的守护程序模块不会改变症状。
这是一些伪代码来显示流程:
createDaemon()
s = myServer() # subclass of asynchat.async_chat
dropPermissions #setuid nobody
asyncore.loop()
这是事情发生时的 strace 输出:
open("/tmp/MailboxDir/cur/1462562219.M374224P24795Q1.i7", O_RDONLY|O_LARGEFILE) = 8
fstat64(8, {st_mode=S_IFREG|0644, st_size=36, ...}) = 0
...
...
read(8, "To: you\nFrom: me\nSubject: test\n\n"..., 8192) = 36
read(8, "", 4096) = 0
read(8, "", 8192) = 0
close(8) = 0
...
stat64("/tmp/MailboxDir/cur/1462562219.M374224P24795Q1.i7", {st_mode=S_IFREG|0644, st_size=36, ...}) = 0
send(7, "+OK POP3 server ready\r\n", 23, 0) = 23
select(8, [4 5 6 7], [], [4 5 6 7], {30, 0}) = -1 EBADF (Bad file descriptor)
省略的部分涉及加载 parser.py 和 feedparser.py 模块,它们本身使用 mmap2 打开和关闭几个文件描述符。
因此电子邮件文件的 fd 已关闭,但不久之后,该 fd 将显示在选择列表中。我不清楚 fd 是如何添加到异步通道映射中的,或者为什么它在 close() 之后仍然存在。我不清楚哪个 fd 现在无效。也不清楚为什么在 shell 进程中运行时不会发生这种情况。
如果我使用线程,我可以看到这种情况,但我快速浏览了导入的模块,没有看到任何表明它们启动了单独的线程或进程的信息。此外,如果我继续以 root 身份运行而不是放弃给任何人,则行为不会改变。
肯定 asyncore 和邮箱模块在其他守护进程中使用。我觉得我一定遗漏了一些明显的东西。