Windows下Python如何创建内存型命名临时文件实现跨进程访问
问题说明
在Windows平台开发Python项目时,若需要通过文件实现跨进程通信,受限于外部进程通过subprocess.run()启动、仅支持传入文件系统路径作为参数的限制,无法直接传递内存中的文件描述符。Linux下可通过os.mkfifo()创建命名管道实现内存级、带路径的共享文件,但Windows不支持该接口。
原有基于tempfile的实现会将文件写入物理磁盘,代码如下:
import tempfile import subprocess import os fd = tempfile.NamedTemporaryFile(mode="w+t", delete=False) # 获取文件描述符 file_path = fd.name # 获取文件路径 # 写入字符串数据后关闭文件 fd.write(data) fd.close() # 外部进程访问文件 output = subprocess.run( [program, file_path], stdout=subprocess.PIPE).stdout.decode('utf-8') # 清理临时文件 os.remove(file_path) print("外部进程输出:", output)
目标是修改代码,让文件写入操作直接在内存中完成,不产生磁盘IO。
可行方案
Windows没有开箱即用的、类似Linux/dev/shm的全局内存文件系统,根据场景可选择以下两种成熟方案:
方案1:Windows命名管道(推荐,纯内存、无额外系统配置)
Windows原生提供命名管道(Named Pipe)机制,管道数据完全存储在内存中,不会写入磁盘,同时拥有标准的文件系统路径,格式为\\.\pipe\自定义名称,绝大多数支持读取本地文件的程序都可以正常识别该路径。
该方案仅需要安装pywin32依赖(执行pip install pywin32即可),修改后的代码如下:
import os import subprocess import win32pipe import win32file # 生成唯一管道路径,避免重名冲突 pipe_path = f"\\\\.\\pipe\\py_tmp_{os.getpid()}" # 创建字节模式的命名管道 pipe_handle = win32pipe.CreateNamedPipe( pipe_path, win32pipe.PIPE_ACCESS_OUTBOUND, win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT, 1, # 最大连接实例数 65536, # 输出缓冲区大小(64KB,足够存放小型临时数据) 65536, # 输入缓冲区大小 0, # 默认超时时间 None ) # 先启动外部进程,避免管道写入操作阻塞 proc = subprocess.Popen( [program, pipe_path], stdout=subprocess.PIPE ) # 建立管道连接、写入数据后关闭写端 win32pipe.ConnectNamedPipe(pipe_handle, None) win32file.WriteFile(pipe_handle, data.encode("utf-8")) win32file.CloseHandle(pipe_handle) # 读取外部进程输出 output = proc.stdout.read().decode("utf-8") proc.wait() print("外部进程输出:", output)
注意:命名管道的读写顺序和普通文件有区别,必须先启动读取数据的外部进程,再向管道写入数据,否则会触发永久阻塞。
方案2:挂载内存盘存放临时文件(完全兼容原有代码)
如果不想修改现有业务逻辑、要求临时文件完全符合普通文件的读写行为,可以先在Windows上挂载一个小型内存盘(RAM Disk),再将临时文件的生成目录指定到内存盘即可。
操作步骤:
- 使用ImDisk等工具创建一个容量几十MB的内存盘,系统会自动分配新的盘符(例如
R:\) - 仅需要修改原有代码中
NamedTemporaryFile的dir参数,指向内存盘路径:
# 假设内存盘盘符为R: fd = tempfile.NamedTemporaryFile(mode="w+t", delete=False, dir="R:\\")
这种方式下所有临时文件的读写都发生在内存中,不会写入物理磁盘,原有代码的其他逻辑完全不需要调整,兼容性最好。
避坑提示:
io.StringIO、io.BytesIO这类纯内存流没有关联文件系统路径,无法被外部进程通过路径识别访问,完全不适用该场景。
内容的提问来源于stack exchange,提问作者Aeryan

