程序Foo定期更新文件并调用我的C程序栏来处理文件。
问题是Foo可能会更新文件,调用Bar来处理它,而当Bar读取文件时,Foo可能会再次更新文件。
Bar是否可以以不一致的状态读取文件,例如读第一个Foo写的文件的前半部分和第二个Foo写的另一半?如果是这样,假设我只能修改Bar的代码,我该怎么做呢?
答案 0 :(得分:1)
通常,Foo不应该只是一次又一次地重写文件的内容,而是创建一个新的临时文件,并在完成后用临时文件替换旧文件(使用link()
)。在这种情况下,只需打开文件(在任何时间点)将为读者提供内容的一致快照,因为POSIX文件系统的典型工作方式。 (打开文件后,即使文件被删除或替换,文件描述符也会引用相同的inode / contents;只有在删除/替换文件的最后一个打开文件描述符关闭后才会释放磁盘空间。)
如果Foo反复重写同一个文件(没有临时文件),建议的解决方案是让Foo和Bar使用基于fcntl()
的建议锁定。 (但是,使用临时文件并在完成后将其重命名/链接到实际文件上会更好。)
(虽然基于flock()
的锁定可能看起来更容易,但它实际上是一个猜测游戏是否适用于NFS挂载。fcntl()
有效,除非NFS服务器配置为不支持锁定。实际上,在某些商业网站主机上存在一些问题。)
如果您无法修改Foo的行为,并且它不使用建议锁定,那么Linux中仍有一些选项。
如果Foo关闭了文件 - 即Bar是唯一一个打开文件的人 - 那么采用独占文件租约(使用fcntl(descriptor, F_SETLEASE, F_WRLCK)
)是一个可行的解决方案。你只能获得独家文件租约如果descriptor
是文件上唯一的开放描述符,并且该文件的所有者用户与进程UID相同(或者进程具有CAP_LEASE
能力)。如果任何其他进程尝试打开或者截断文件,租约所有者会收到信号通知(默认为SIGIO
),并且最多可以/proc/sys/fs/lease-break-time
秒降级或释放租约。开启者会被阻止持续时间,这样Bar就可以取消处理,或复制文件以供以后处理。
Bar的另一个选择是相当暴力。它可以监视文件说每秒一次,当文件足够老时 - 比如几秒钟 - 通过发送SIGSTOP
信号暂停Foo,检查/proc/FOOPID/stat
直到它停止并重新检查文件统计信息以验证它是否仍旧,直到对其进行临时复制(在内存或磁盘上)进行处理。读取/复制文件后,Bar可以通过发送SIGCONT
信号让Foo继续。
某些文件系统可能支持文件快照,但在我看来,上述其中一个比依赖非标准文件系统支持正常运行要熟练得多。如果Foo无法修改为合作,那么现在是时候将它重构出画面了。你不想成为一个不受控制的黑匣子的人质,所以越早用更好的用户/管理员友好的东西替换它,你就会越长久。
答案 1 :(得分:0)
如果没有Foo的合作,这很难做到。 Unix有两种主要的文件锁定方式:
理想情况下,您可以在协作模式(咨询锁定)中使用其中任何一个,其中所有参与者都尝试获取锁定,并且每次只能获得一个锁定。
没有其他程序的合作,据我所知,你唯一的办法是强制锁定,如果你允许的话,可以使用 fcntl 在文件系统上,但是联机帮助页提到Linux实现是不可靠的。
答案 2 :(得分:0)
在所有UN * X系统中,保证以原子方式发生的是write(2)
或read(2)
系统调用。内核甚至将文件inode锁定在内存中,所以当你read(2)
或write(2)
它时,它就不会改变。
要获得更多空间原子性,您必须锁定整个文件。您可以使用可用的文件锁定工具来锁定文件的不同区域。一些是建议性的(你可以强制跳过它们)而其他的是强制性的(你被阻止,直到另一方解锁文件区域)
请参阅fcntl(2)
以及选项F_GETLK
,F_SETLK
和F_SETLKW
以获取锁定信息,分别设置锁定以进行读取或写入。