Python 何时将文件写入磁盘?

4
我有一个与配置文件交互的库。当导入该库时,初始化代码会读取配置文件,可能会更新它,然后将更新后的内容写回到文件中(即使没有更改)。
很少情况下,我会遇到配置文件内容突然消失的问题。具体来说,当我连续多次运行一个短脚本(使用该库),数千次之后,就会发生这种情况。它从不发生在同一目录中,这让我相信这是一个有点随机的问题--特别是IO的竞争条件。
由于无法可靠地重现这个问题,而且只在某些系统上出现,所以这很难调试。我对可能发生的事情有怀疑,但我想看看我关于Python文件IO的理解是否正确。
因此问题是: Python程序实际上什么时候将文件内容写入磁盘?我认为,文件关闭时文件内容应该已经写入磁盘,但是我无法解释这个错误。当Python关闭文件时,它是否会将内容刷新到磁盘本身,还是仅将其排队到文件系统?文件内容是否可能在Python终止后写入磁盘?我可以通过使用fp.flush(); os.fsync(fp.fileno())(其中 fp 是文件句柄)来避免此问题吗?
如果有关系,我正在Unix系统(特别是Mac OS X)上编程。编辑:还要注意,这些进程不会同时运行。
附录:这里是我怀疑的具体竞争条件:
  1. 调用进程#1。
  2. 进程#1以读模式打开配置文件,并在完成后关闭它。
  3. 进程#1以写模式打开配置文件,擦除其所有内容。删除内容已同步到磁盘。
  4. 进程#1将新内容写入文件句柄并关闭它。
  5. 进程#1:在关闭文件时,Python告诉操作系统将这些内容排队写入磁盘。
  6. 进程#1关闭并退出
  7. 调用进程#2
  8. 进程#2以读模式打开配置文件,但新内容尚未同步。进程#2看到一个空文件。
  9. 操作系统最终完成将内容写入磁盘,之前进程2已经读取了文件
  10. 进程#2认为文件为空,为配置文件设置默认值。
  11. 进程#2将其版本的配置文件写入磁盘,覆盖上一个版本。

5
不,当Python关闭文件时,文件已经被刷新到磁盘上了。我想说的是,进程#2比你想象中早打开了这个文件。 - Martijn Pieters
2
如果多个进程同时访问一个文件,并且其中至少一个进程对其进行写操作,则必须同步这些进程以获得一致的结果。这并不是Python特有的。 - Fred Foo
2
即使操作系统没有将数据写入磁盘,只要从Python中刷新出来,它也会保证将文件内容返回给第二个进程,因为有一个内存缓存被所有访问该文件的人共享。(除非您在共享文件系统上运行不同机器上的进程且未配置一致性,或者存在覆盖文件的竞争条件。) - nos
1
问题的一个可能性是:只要你用 'w' 打开,内容就会被截断。如果另一个进程在它被再次写入之前从中读取,它将为空。 - cmd
2
@misfavor 在进程1中添加一些调试功能,检查其生成的文件是否为空,并将进程1和2之间的文件名进行关联,以便知道哪个运行产生了错误的文件。 - nos
显示剩余3条评论
1个回答

2
几乎可以肯定不是Python的问题。如果Python关闭文件,或者干净地退出(而不是被信号杀死),那么操作系统将拥有文件的新内容。任何后续打开都应返回新内容。一定有更复杂的事情发生了。以下是一些想法。
1. 您所描述的更可能是文件系统错误而不是Python错误,而文件系统错误相当不可能。
2. 如果您的文件实际上驻留在远程文件系统中,则更有可能出现文件系统错误。这些文件是否在远程文件系统中?
3. 所有进程是否使用同一个文件?在该文件上执行“ls -li”以查看其i节点号,并查看其是否会更改。在您的情况下,它不应该更改。是否有可能正在移动文件,移动目录或删除目录并重新创建它们?涉及符号链接吗?
4. 您确定没有程序运行重叠吗?其中任何一个是在带有“&”结尾的shell中运行的(即在后台)?这很容易意味着第二个程序在第一个程序完成之前启动。
5. 是否有其他程序写入同一个文件?
这不是您的问题,但是如果需要原子更改(以便并行运行的任何程序仅看到旧版本或新版本,永远不会看到空文件),则实现方法是将新内容写入另一个文件(例如“foo.tmp”),然后执行os.rename(“foo.tmp”,“foo”)。重命名是原子性的。

这可能是我所能期望的最好的答案了。谢谢! - HardlyKnowEm
事实证明,一名实习生编辑了一个库函数以生成另一个Python子进程(使用相同的模块)。在我回滚该更改后,问题似乎消失了。这也可能解释了为什么问题只发生在实习生的计算机上。 - HardlyKnowEm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接