场景:我有许多进程正在运行,需要通过网络获取文件。如果文件已经下载,我希望它缓存在磁盘上。如果另一个进程正在下载该文件,请阻止,直到下载完成为止。
我一直在努力寻找最简单的方法。显而易见的方法是:
create file w/ an exclusive lock active on it only if it doesn't exist (O_CREAT | O_EXCL)
if file exists already:
open file and acquire exclusive lock
else:
download to newly created file
release lock
该系统在无比赛条件的情况下实现了上述目标
不幸的是,我找不到关于如何使用open()等创建锁定在Linux中的文件的文档。如果我将创建步骤拆分为:
open w/ O_CREAT | O_EXCL
flock
现在,create和lock之间存在竞争条件(非创建进程在创建者之前获取锁)。
我意识到我可以为每个文件使用一个外部锁定文件(例如filename+'.lock),这是我在尝试创建文件名之前获得的,但这感觉。。不雅(我现在需要担心如何处理真正有.lock后缀的文件!)
是否有原子式创建和锁定它(正如Windows提供的那样),或者外部锁定文件方法几乎是标准/必需的?
种族仍然存在。如果文件可能存在,也可能不存在,那么在尝试锁定它之前,您必须测试它的存在。但是,如果文件是您的互斥对象,那么您不可能这样做,并且"如果文件已经存在"(false)和"下载到新创建的文件"之间的空格是不受限制的。另一个过程可能会出现,在下载开始之前创建文件并开始下载,你会把它搞砸
这里基本上不使用fcntl锁,使用文件本身的存在。如果文件已经存在,则带有O_CREAT和O_EXCL的open()
将失败,并告诉您其他人先到达了那里。
为什么不使用lockfile实用程序?
示例
假设您希望确保对文件的访问"重要"串行化,即不应超过一个程序或shell脚本允许访问它。为了简单起见,让我们假设它是shell脚本。在这种情况下,你可以这样解决:
...
lockfile important.lock
...
access_"important"_to_your_hearts_content
...
rm -f important.lock
...
我目前正在努力解决类似的问题,这就是这让我想到了你的问题。在我看来,本质是:
int fd = open(path, O_CREAT|O_RDWR|O_EXCL, mode);
if (fd == -1)
{
/* File already exists. */
the_file_already_exists(fd);
}
else
{
/* I just now created the file. Now I'll lock it. */
/* But first I'll deliberately create a race condition!! */
deliberately_fork_another_process_that_handles_file(path);
int code = flock(fd,LOCK_EX);
if (code < 0)
{
perror("flock");
exit(1);
}
/* I now have the exclusive lock. I can write to the file at will --
or CAN I?? See below. */
write_to_the_file_at_will(fd);
}
很明显,在现实生活中,我从来不会故意创造那种种族条件,但在现实系统中,它的等价物肯定会意外发生。那个例如,其他进程可能打开文件进行读取,获取共享锁定它,然后读取文件。它会看到一个空文件。这可能意味着写入操作正在进行,但这可能意味着文件只是空的,这是正确的,也是最终的答案。
如果不允许空文件,则读取器可以简单地按照以下方式操作如果文件丢失,它会正常工作。毕竟,如果读者已经开始一毫秒之前,它无论如何都无法打开文件。在这个如果阅读器打开文件后需要检查文件是否为空。
如果允许空文件,那么你就有点进退两难了,而我没有这是一个现成的答案。
我遇到的问题是,当第一次创建文件时,我想写一些默认值,因为我想"自动初始化"一个新的系统,而无需预先创建可能需要的所有文件。那个其他处理文件的进程可能本身已经初始化了它!据我所知,在此期间可能还运行了其他三个进程更改了值。在这种情况下,我当然不想"写入文件随意",因为我会把所有这些都打掉更改。
我想答案是我上面的代码,以确保文件在写入之前为空。如果它不是空的,那么代码应该正常工作就像文件已经存在一样:即,它应该调用:
the_file_already_exists(fd);
也许所有这些讨论的底线是以任何方式处理该文件都应该检查该文件是否为空照着不过,如果允许空文件,那么我还不能思考任何有保证的解决方案。如果有创建文件并将其锁定为单个原子序列的方法,但我没有我想有办法做到这一点。