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
相关产品推荐
相关产品推荐

