Python子进程:如何/何时关闭文件?

问题描述 投票:0回答:1

我想知道为什么子流程会打开这么多文件。我有一个示例,其中某些文件似乎永远保持打开状态(在子进程完成后,甚至在程序崩溃之后)。

请考虑以下代码:

import aiofiles
import tempfile

async def main():
    return [await fds_test(i) for i in range(2000)]

async def fds_test(index):
    print(f"Writing {index}")
    handle, temp_filename = tempfile.mkstemp(suffix='.dat', text=True)
    async with aiofiles.open(temp_filename, mode='w') as fp:
        await fp.write('stuff')
        await fp.write('other stuff')
        await fp.write('EOF\n')

    print(f"Reading {index}")
    bash_cmd = 'cat {}'.format(temp_filename)
    process = await asyncio.create_subprocess_exec(*bash_cmd.split(), stdout=asyncio.subprocess.DEVNULL, close_fds=True)
    await process.wait()
    print(f"Process terminated {index}")

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

此顺序依次产生一个进程。我希望与此同时打开的文件数也将是1。事实并非如此,有时我会收到以下错误:

/Users/cglacet/.pyenv/versions/3.8.0/lib/python3.8/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1410             # Data format: "exception name:hex errno:description"
   1411             # Pickle is not used; it is complex and involves memory allocation.
-> 1412             errpipe_read, errpipe_write = os.pipe()
   1413             # errpipe_write must not be in the standard io 0, 1, or 2 fd range.
   1414             low_fds_to_close = []

OSError: [Errno 24] Too many open files

我尝试运行不带stdout=asyncio.subprocess.DEVNULL选项的相同代码,但仍然崩溃。 This answer suggested可能是问题出处,错误也指向errpipe_read, errpipe_write = os.pipe()行。但这似乎不是问题所在(在没有该选项的情况下运行会产生相同的错误)。

如果需要更多信息,这是lsof | grep python输出的概述:

python3.8 19529 cglacet    7u      REG                1,5        138 12918796819 /private/var/folders/sn/_pq5fxn96kj3m135j_b76sb80000gp/T/tmpuxu_o4mf.dat
# ... 
# ~ 2000 entries later : 
python3.8 19529 cglacet 2002u      REG                1,5        848 12918802386 /private/var/folders/sn/_pq5fxn96kj3m135j_b76sb80000gp/T/tmpcaakgz3f.dat

这些是我的子进程正在读取的临时文件。 lsof的其余输出似乎是合法的东西(打开了库,例如pandas /numpy/scipy/等)。

[现在,我有些疑问:问题可能出自aiofiles异步上下文管理器吗?也许是不是不关闭文件而不是create_subprocess_exec的人?

这里有一个类似的问题,但是没有人真正尝试解释/解决问题(仅建议增加限制):Python Subprocess: Too Many Open Files。我真的很想了解发生了什么,我的首要目标不一定是暂时解决问题(将来,我希望能够根据需要多次运行函数fds_test)。我的目标是拥有一个功能符合预期的功能。我可能必须更改我的期望或代码,这就是为什么我问这个问题。


如注释here中的建议,我还尝试运行python -m test test_subprocess -m test_close_fds -v,它给出:

== CPython 3.8.0 (default, Nov 28 2019, 20:06:13) [Clang 11.0.0 (clang-1100.0.33.12)]
== macOS-10.14.6-x86_64-i386-64bit little-endian
== cwd: /private/var/folders/sn/_pq5fxn96kj3m135j_b76sb80000gp/T/test_python_52961
== CPU count: 8
== encodings: locale=UTF-8, FS=utf-8
0:00:00 load avg: 5.29 Run tests sequentially
0:00:00 load avg: 5.29 [1/1] test_subprocess
test_close_fds (test.test_subprocess.POSIXProcessTestCase) ... ok
test_close_fds (test.test_subprocess.Win32ProcessTestCase) ... skipped 'Windows specific tests'

----------------------------------------------------------------------

Ran 2 tests in 0.142s

OK (skipped=1)

== Tests result: SUCCESS ==

1 test OK.

Total duration: 224 ms
Tests result: SUCCESS

所以似乎文件应该被正确关闭,我在这里有点丢失。

python subprocess pipe
1个回答
0
投票

问题不是来自create_subprocess_exec,此代码中的问题是tempfile.mkstemp()实际上打开了文件:

mkstemp()将包含操作系统级别句柄的元组返回到打开的文件(如os.open()将返回的…)

我以为只会创建文件。为了解决我的问题,我仅向tempfile.mkstemp()添加了一个电话。

© www.soinside.com 2019 - 2024. All rights reserved.