简体   繁体   English

Python 子进程:打开的文件太多

[英]Python Subprocess: Too Many Open Files

I am using subprocess to call another program and save its return values to a variable.我正在使用 subprocess 调用另一个程序并将其返回值保存到变量中。 This process is repeated in a loop, and after a few thousands times the program crashed with the following error:这个过程在循环中重复,几千次后程序崩溃并出现以下错误:

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

Code:代码:

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()

In Mac OSX (El Capitan) See current configuration:在 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

Set open files value to 10K :打开的文件值设置为 10K :

#ulimit -Sn 10000

Verify results:验证结果:

#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

I guess the problem was due to the fact that I was processing an open file with subprocess:我想问题是由于我正在使用子进程处理打开的文件:

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)

Here the cmd variable contain the name of a file that has just been created but not closed.这里的 cmd 变量包含刚刚创建但未关闭的文件的名称。 Then the subprocess.Popen calls a system command on that file.然后subprocess.Popen对该文件调用系统命令。 After doing this for many times, the program crashed with that error message.多次执行此操作后,程序因该错误消息而崩溃。

So the message I learned from this is所以我从中学到的信息是

Close the file you have created, then process it关闭您创建的文件,然后对其进行处理

您可以尝试提高操作系统的打开文件限制:

ulimit -n 2048

As others have noted, raise the limit in /etc/security/limits.conf and also file descriptors was an issue for me personally, so I did正如其他人所指出的,提高 /etc/security/limits.conf 中的限制以及文件描述符对我个人来说是一个问题,所以我做了

sudo sysctl -w fs.file-max=100000 

And added to /etc/sysctl.conf:并添加到/etc/sysctl.conf:

fs.file-max = 100000

Reload with:重新加载:

sudo sysctl -p

Also if you want to make sure that your process is not affected by anything else (which mine was), use此外,如果您想确保您的流程不受其他任何东西(我的)的影响,请使用

cat /proc/{process id}/limits 

to find out what the actual limits of your process are, as for me the software running the python scripts also had its limits applied which have overridden the system wide settings.找出你的进程的实际限制是什么,对我来说,运行 python 脚本的软件也应用了它的限制,这些限制覆盖了系统范围的设置。

Posting this answer here after resolving my particular issue with this error and hopefully it helps someone.在解决了我与此错误有关的特定问题后,在此处发布此答案,希望对某人有所帮助。

A child process created by Popen() may inherit open file descriptors (a finite resource) from the parent. Popen()创建的子进程可以从父进程继承打开的文件描述符(有限资源)。 Use close_fds=True on POSIX (default since Python 3.2), to avoid it.在 POSIX 上使用close_fds=True (自 Python 3.2 起默认),以避免它。 Also, "PEP 0446 -- Make newly created file descriptors non-inheritable" deals with some remaining issues (since Python 3.4) .此外, “PEP 0446 - 使新创建的文件描述符不可继承”处理一些遗留问题(自 Python 3.4 起)

Maybe you are invoking the command multiple times.也许您多次调用该命令。 If so, each time you're doing stdout=subprocess.PIPE .如果是这样,每次你在做stdout=subprocess.PIPE Between each call try doing p.stdout.close() .在每次通话之间尝试执行p.stdout.close()

Use context managers instead:改用上下文管理器:

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()

This will close p.stdout and p.stderr after the last line.这将在最后一行之后关闭p.stdoutp.stderr

Related codes in Python: https://github.com/python/cpython/blob/208a7e957b812ad3b3733791845447677a704f3e/Lib/subprocess.py#L1031-L1038 Python中的相关代码: https : //github.com/python/cpython/blob/208a7e957b812ad3b3733791845447677a704f3e/Lib/subprocess.py#L1031-L1038

Related document: https://docs.python.org/3/library/subprocess.html#subprocess.Popen相关文档: https : //docs.python.org/3/library/subprocess.html#subprocess.Popen

If you are working on Linux you can easily debug this problem如果你在 Linux 上工作,你可以很容易地调试这个问题

1 - Start the command that eventually will fail due Too Many Open Files in a terminal. 1 - 启动最终会因终端中打开的文件太多而失败的命令。

python -m module.script

2 - Let it run for a while (so it can start opening the actual files) and whenever you believe it has done so just press CTRL+Z so the process will be suspended. 2 - 让它运行一段时间(以便它可以开始打开实际文件),只要您认为它已经完成,只需按CTRL+Z暂停该进程。 You will have an output with the process id.您将获得带有进程 ID 的输出。

^Z
[2]  + 35245 suspended  python -m module.script

35245 is your PID. 35245是您的 PID。

3 - Now you can check what files are actually opened and not closed. 3 - 现在您可以检查哪些文件实际上是打开的而不是关闭的。

ls -alht /proc/35245/fd/

In my case I was doing something very similar to the original post, but I was creating a temporarily file with tempfile.mkstemp() before adding some data and actually running the subprocess.Popen .在我来说,我做的非常类似原职的东西,但我创建一个临时使用文件tempfile.mkstemp()增加了一些数据和实际运行之前subprocess.Popen

In this case you need to close the file twice, once for adding the information and the second one due mkstemp在这种情况下,您需要关闭文件两次,一次用于添加信息,第二次用于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

Raise the limit to eg 32768 by adding the following lines to /etc/security/limits.conf :通过将以下行添加到/etc/security/limits.conf将限制提高到例如32768

* soft nofile 32768
* hard nofile 32768

Then, run ulimit -n 32768 as well.然后,也运行ulimit -n 32768

Source: Dan D.'s comment .资料来源: Dan D. 的评论

opens file in subprocess.在子进程中打开文件。 It is blocking call.它正在阻止呼叫。

ss=subprocess.Popen(tempFileName,shell=True)
 ss.communicate()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM