您询问了NFS。这种代码很可能会在NFS下崩溃,因为检查noclobber
涉及两个单独的NFS操作(检查文件是否存在,创建新文件),并且来自两个单独的NFS客户端的两个进程可能进入竞争状态,而这两个进程都成功(都验证B.part
尚不存在,然后都继续成功创建它,结果是它们彼此覆盖。)
实际上,您并不需要对要写入的文件系统是否支持noclobber
原子性进行通用检查。您可以检查文件系统类型,是否为NFS,但这将是一种启发式方法,不一定是一种保证。像SMB / CIFS(Samba)这样的文件系统可能也会遇到相同的问题。通过FUSE公开的文件系统可能无法正常运行,但这主要取决于实现。
可能更好的方法是B.part
通过使用唯一的文件名(通过与其他代理协作)来避免步骤中的冲突,从而使您不必依赖noclobber
。例如,您可以在主机名,PID和时间戳(可能是随机数)中添加主机名,PID和时间戳(作为文件名的一部分)。由于在任何给定时间,主机上都应有一个进程在特定PID下运行。保证唯一性。
因此,以下任何一项:
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
# Maybe check for existance of B again, remove
# the temporary file and bail out in that case.
mv B.part."$unique" B
# mv (rename) should always succeed, overwrite a
# previously copied B if one exists.
要么:
test -f B && continue # skip already existing
unique=$(hostname).$$.$(date +%s).$RANDOM
cp A B.part."$unique"
if ln B.part."$unique" B ; then
echo "Success creating B"
else
echo "Failed creating B, already existed"
fi
# Both cases require cleanup.
rm B.part."$unique"
因此,如果两个代理之间存在争用条件,则它们都将继续执行该操作,但是最后一个操作将是原子操作,因此B存在且具有A的完整副本,或者B不存在。
您可以通过在复制之后和mv
or ln
运算之前再次检查来减小种族的大小,但是那里的种族情况仍然很小。但是,不管竞争条件如何,假设两个进程都试图从A(或从有效文件的副本作为源)创建B的内容,则B的内容应保持一致。
请注意,在第一种情况下mv
,如果存在种族,则最后一个过程是获胜的过程,因为rename(2)将原子替换现有文件:
如果newpath已经存在,它将被原子替换,这样,尝试访问newpath的另一个进程将找不到它。[...]
如果存在newpath,但由于某种原因操作失败,请rename()
确保将newpath实例保留在原处。
因此,很有可能当时消耗B的进程在此过程中可能会看到它的不同版本(不同的inode)。如果编写者都在尝试复制相同的内容,而阅读者只是在使用文件的内容,那可能很好,如果他们为具有相同内容的文件获得了不同的inode,那么他们会同样高兴。
使用硬链接的第二种方法看起来更好,但是我记得在许多并发客户端的NFS上以紧密循环对硬链接进行了实验,并计算了成功,并且那里似乎仍然存在一些竞争状况,似乎两个客户端发出了硬链接在相同的目的地同时进行的操作似乎都成功了。(此行为可能与特定的NFS服务器实现YMMV有关。)在任何情况下,这可能是同一种竞争条件,在这种情况下,如果文件过多,最终可能会为同一个文件获得两个独立的inode作家之间的并发触发这些种族条件。如果您的作者是一致的(都将A复制到B),而您的读者只是在消费内容,那可能就足够了。
最后,您提到了锁定。不幸的是,至少在NFSv3中严重缺乏锁定(不确定NFSv4,但我敢打赌它也不好。)如果您正在考虑锁定,则应该研究用于分布式锁定的不同协议,可能与实际的文件副本,但这既破坏性,复杂又容易导致死锁等问题,所以我想最好避免。
有关NFS原子性主题的更多背景,您可能需要阅读Maildir邮箱格式,该格式是为避免锁定而创建的,即使在NFS上也能可靠地工作。这样做是通过在各处保留唯一的文件名来实现的(因此,甚至在最后都不会得到最终的B。)
也许你会更有趣您的特定情况下,的Maildir ++格式扩展的Maildir增加对邮箱配额支持和原子更新与固定名称的文件的邮箱内(这样可能会更接近您B.)我想的Maildir ++尝试这样做附加,这在NFS上并不是很安全,但是有一种重新计算方法,它使用与此类似的过程,并且可以作为原子替换有效。
希望所有这些指针将是有用的!