我正在使用子进程调用另一个程序并将其返回值保存到变量中。这个过程循环重复,几千次后程序崩溃并出现以下错误:
Traceback (most recent call last):
File "./extract_pcgls.py", line 96, in <module>
SelfE.append( CalSelfEnergy(i) )
File "./extract_pcgls.py", line 59, in CalSelfEnergy
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
File "/usr/lib/python3.2/subprocess.py", line 745, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.2/subprocess.py", line 1166, in _execute_child
errpipe_read, errpipe_write = _create_pipe()
OSError: [Errno 24] Too many open files
代码:
cmd = "enerCHARMM.pl -parram=x,xtop=topology_modified.rtf,xpar=lipid27_modified.par,nobuildall -out vdwaals {0}".format(cmtup[1])
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
out, err = p.communicate()
在 Mac OSX (El Capitan) 中查看当前配置:
#ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 256
pipe size (512 bytes, -p) 1
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 709
virtual memory (kbytes, -v) unlimited
将 打开文件 值设置为 10K :
#ulimit -Sn 10000
验证结果:
#ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
file size (blocks, -f) unlimited
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 10000
pipe size (512 bytes, -p) 1
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 709
virtual memory (kbytes, -v) unlimited
我猜问题是由于我正在使用子进程处理打开的文件:
cmd = "enerCHARMM.pl -par param=x,xtop=topology_modified.rtf,xpar=lipid27_modified.par,nobuildall -out vdwaals {0}".format(cmtup[1])
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
这里的 cmd 变量包含刚刚创建但尚未关闭的文件的名称。然后
subprocess.Popen
对该文件调用系统命令。多次执行此操作后,程序崩溃并显示该错误消息。
所以我从中学到的信息是
关闭您创建的文件,然后处理它
您可以尝试提高操作系统的打开文件限制:
ulimit -n 2048
正如其他人所指出的,提高 /etc/security/limits.conf 中的限制,文件描述符对我个人来说也是一个问题,所以我这样做了
sudo sysctl -w fs.file-max=100000
并添加到/etc/sysctl.conf:
fs.file-max = 100000
重新加载:
sudo sysctl -p
此外,如果您想确保您的流程不受其他任何事情(我的流程)的影响,请使用
cat /proc/{process id}/limits
找出你的进程的实际限制是什么,对我来说,运行 python 脚本的软件也有其限制,这些限制覆盖了系统范围的设置。
解决我的特定问题后,将此答案发布到此处,希望它对某人有所帮助。
由
Popen()
创建的子进程可以从父进程继承打开文件描述符(有限资源)。在 POSIX 上使用 close_fds=True
(自 Python 3.2 起默认),以避免它。另外,“PEP 0446 -- 使新创建的文件描述符不可继承”处理一些剩余问题(自 Python 3.4 起)。
也许您多次调用该命令。如果是这样,每次您都这样做
stdout=subprocess.PIPE
。在每次通话之间尝试执行 p.stdout.close()
。
改用上下文管理器:
cmd = "enerCHARMM.pl -param=x,xtop=topology_modified.rtf,xpar=lipid27_modified.par,nobuildall -out vdwaals {0}".format(cmtup[1])
with subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) as p:
out, err = p.communicate()
这将在最后一行之后关闭
p.stdout
和 p.stderr
。
相关文档:https://docs.python.org/3/library/subprocess.html#subprocess.Popen
如果您在 Linux 上工作,您可以轻松调试这个问题
1 - 启动最终会因终端中的打开文件过多而失败的命令。
python -m module.script
2 - 让它运行一段时间(这样它就可以开始打开实际文件),每当您认为它已经完成时,只需按
CTRL+Z
,进程就会暂停。您将得到带有进程 ID 的输出。
^Z
[2] + 35245 suspended python -m module.script
35245
是您的 PID。
3 - 现在您可以检查哪些文件实际打开且未关闭。
ls -alht /proc/35245/fd/
就我而言,我所做的事情与原始帖子非常相似,但在添加一些数据并实际运行
tempfile.mkstemp()
之前,我使用 subprocess.Popen
创建了一个临时文件。
在这种情况下,您需要关闭文件两次,一次是为了添加信息,第二次是由于
mkstemp
fd, path = tempfile.mkstemp()
with open(path, "wb") as f:
f.write(bytes('my data', encoding='utf8'))
f.close() # this is one time
process = subprocess.Popen("my command that requires the previous file" ,[...])
os.close(fd) # this is second time and the one I missed
将限制提高到例如
32768
将以下行添加到 /etc/security/limits.conf
:
* soft nofile 32768
* hard nofile 32768
然后,也运行
ulimit -n 32768
。
来源:Dan D. 的评论。
我提高了
/etc/security/limits.conf
的限制,但我的Python进程仍然只有最大1024
打开文件的限制。
我的 python 进程作为服务运行,结果 systemd 注入
1024
作为默认值,从而覆盖 limits.conf
中的设置。我需要将其添加到 /etc/systemd/system/my.service
:
[Service]
LimitNOFILE=327680
在子进程中打开文件。正在阻止呼叫。
ss=subprocess.Popen(tempFileName,shell=True)
ss.communicate()