Python中with语句结合os.fork出现重复写入问题的咨询
Why Does
with open() + os.fork() Produce Unexpected File Content? 这是个很典型的Python用户态缓冲与系统fork机制冲突的问题,咱们来掰扯清楚背后的原因:
1. What's Wrong with the with open() Version?
你用with open()创建的是Python的高层文本文件对象(io.TextIOWrapper),它默认带有用户态缓冲(文本模式下通常是行缓冲或全缓冲)。整个过程的问题出在缓冲的复制上:
- 当你执行
f.write(m)(即写入foo\n),因为是行缓冲,这段内容并没有立刻写入磁盘,而是暂存在Python进程的用户态缓冲里。 - 调用
os.fork()后,子进程会完整复制父进程的地址空间——包括这个文件对象的缓冲状态,也就是说子进程的文件对象缓冲里也躺着一份foo\n。 - 随后父进程和子进程各自执行
f.write(n)(写入bar\n),当with块结束时,两个进程都会自动调用f.close(),触发缓冲刷新:父进程把自己缓冲里的foo\nbar\n写入磁盘,子进程也会把自己缓冲里的foo\nbar\n写入磁盘。最终文件就变成了两次foo\nbar\n的拼接,也就是你看到的foo bar foo bar。
2. Why Does the os.open() Version Work as Expected?
os.open()和os.write()是直接调用系统级API,操作的是底层文件描述符,没有Python层面的用户态缓冲:
os.write(fd, m)会直接把foo\n写入内核的文件缓冲区(注意是内核维护的,不是Python进程的用户态缓冲),此时文件的偏移量已经更新到foo\n之后的位置。- 调用
os.fork()后,子进程继承了这个文件描述符,并且内核级的文件偏移量是父子进程共享的。 - 父进程执行
os.write(fd, n)时,从当前偏移量写入bar\n;子进程执行os.write(fd, n)时,同样从这个共享的偏移量继续写入,最终就得到了预期的foo\nbar\nbar\n。
3. How to Fix the with open() + os.fork() Case?
不是with语句本身有问题,而是它封装的高层文件对象的缓冲机制在fork场景下需要特殊处理。你只需要在fork前手动刷新缓冲,把用户态的内容强制写入内核:
import os m = 'foo\n' n = 'bar\n' with open('test2.txt', 'w') as f: f.write(m) f.flush() # 手动刷新缓冲,将foo写入内核缓冲区 os.fork() f.write(n)
这样fork的时候,子进程继承的文件对象缓冲已经是空的,之后父子进程各自写入bar\n,最终文件内容就符合预期了。
内容的提问来源于stack exchange,提问作者binbin
相关产品推荐
相关产品推荐

