You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python子进程文件未关闭致打开文件过多问题排查

问题根源:未关闭mkstemp返回的文件描述符

你遇到的「Too many open files」错误,核心问题既不是aiofiles的异步上下文管理器,也不是subprocess的配置,而是你调用tempfile.mkstemp()后,完全忽略了它返回的操作系统级文件描述符,导致这些描述符一直处于打开状态,最终耗尽了系统的文件句柄上限。

具体原因拆解

tempfile.mkstemp()的设计是返回两个值:

  • 第一个是整数类型的文件描述符(handle):这是操作系统直接分配的文件句柄,不会被Python的垃圾回收自动管理
  • 第二个是临时文件的路径

你的代码里只用到了路径去打开文件,但完全没处理这个handle——每循环一次就会留下一个未关闭的文件描述符,循环2000次就会打开2000个未关闭的文件,直接触发系统的文件句柄限制。

而你用aiofiles.open()打开的是同一个文件,但这是一个全新的文件句柄,异步上下文管理器会正常关闭它;真正泄漏的是mkstemp返回的那个原始文件描述符。

修复代码

只需要在使用完临时文件路径后,手动关闭mkstemp返回的handle即可,记得导入os模块:

import aiofiles
import tempfile
import os  # 新增导入
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')
    os.close(handle)  # 新增:关闭mkstemp返回的文件描述符
    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())

额外建议

如果你不需要手动操作文件描述符,其实可以用tempfile.NamedTemporaryFile替代mkstemp,它会在文件对象关闭时自动清理文件句柄(Unix系统下默认会在关闭时删除文件,若需要保留文件可设置delete=False),能避免手动管理文件描述符的麻烦。

另外你运行的test_close_fds测试是验证subprocess的close_fds参数是否正常工作,但你的问题根本不在subprocess模块,所以这个测试通过完全不影响你的问题排查。

内容的提问来源于stack exchange,提问作者cglacet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 07:52:39