我在Python(2.7)中有一个Web服务器,它使用Popen
将一些工作委托给子进程:
url_arg = "http://localhost/index.html?someparam=somevalue"
call = ('phantomjs', 'some/phantom/script.js', url_arg)
imageB64data = tempfile.TemporaryFile()
errordata = tempfile.TemporaryFile()
p = Popen(call, stdout=imageB64data, stderr=errordata, stdin=PIPE)
p.communicate(input="")
我看到间歇性的问题,在发生了一些Popen
之后(大约64个),进程耗尽了文件描述符并且无法运行 - 它变得完全没有响应并且所有线程似乎都是如果他们试图打开任何文件或套接字,则永远阻止。
(可能相关:phantomjs
子进程将URL调用加载回生成它的服务器。)
基于this Python bug report,我认为我需要在服务器进程内的所有close_fds=True
调用上设置Popen
,以减少文件描述符的泄漏。但是,我不熟悉exec
周围的机制和文件描述符的继承这么多的Popen
文档,上面提到的错误报告中的注释对我来说还不清楚。
听起来它实际上会在执行子进程之前关闭我的进程中所有打开的文件描述符(包括活动请求套接字,日志文件句柄等)。这听起来肯定比泄漏套接字更好,但仍然会导致错误。
然而,在实践中,当我在Web请求期间使用close_fds=True
时,它似乎工作正常,到目前为止,我无法构建一个实际关闭任何其他请求套接字,数据库请求等的场景
文档声明:
如果close_fds为true,则在执行子进程之前将关闭除0,1和2之外的所有文件描述符。
所以我的问题是:它是否安全"并且"纠正"在多线程Python Web服务器中将close_fds=True
传递给Popen
?或者,如果其他请求同时执行文件/套接字IO,我是否应该预期会产生副作用?
答案 0 :(得分:2)
我尝试使用Python 3.2 / 3.3&{39} subprocess32
的{{1}}后端进行以下测试:
subprocess
我在import tempfile
import subprocess32 as subprocess
fp = open('test.txt', 'w')
fp.write("some stuff")
echoed = tempfile.TemporaryFile()
p = subprocess.Popen(("echo", "this", "stuff"), stdout=echoed, close_fds=True)
p.wait()
echoed.seek(0)
fp.write("whatevs")
fp.write(echoed.read())
fp.close()
中得到了some stuffwhatevsecho this stuff
的预期结果。
因此test.txt
中close
的含义似乎不意味着父进程中的打开文件(套接字等)在执行之后将无法使用儿童过程。
另外值得注意的是:close_fds
在POSIX系统上默认为subprocess32
,AFAICT。这对我来说意味着它没有听起来那么危险。
答案 1 :(得分:0)
我怀疑close_fds
解决了文件描述符泄漏到子流程的问题。想象一下打开一个文件,然后使用subprocess
运行一些任务。如果没有close_fds
,文件描述符将被复制到子进程,因此即使父进程关闭了文件,由于子进程,文件仍保持打开状态。现在,假设我们要使用shutil.rmtree
删除具有另一个线程中的文件的目录。在常规文件系统上,这应该不是问题。该目录已按预期删除。但是,当文件驻留在NFS上时,会发生以下情况:首先,Python将尝试删除该文件。由于文件仍在使用中,因此将其重命名为.nfsXXX
,其中XXX
是一个十六进制数字。接下来,Python将尝试删除该目录,但是由于.nfsXXX
文件仍驻留在其中,因此该操作变得不可能。