13

场景:我有许多正在运行的进程需要通过网络获取文件。如果文件已经下载,我希望它缓存在磁盘上。如果另一个进程正在下载文件,则阻塞直到下载完成。

我一直在努力寻找最简单的方法来做到这一点。显而易见的方法是:

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

现在创建和锁定之间存在竞争条件(非创建进程在创建者之前获取锁)。

我意识到我可以为每个文件使用一个外部锁定文件(例如文件名 + '.lock),这是我在尝试创建文件名之前获得的,但这感觉......不优雅(我现在需要担心如何处理实际上有.lock 后缀!)

无论如何都可以原子地创建和锁定它(如 Windows 提供的那样),或者外部 lockfile 方法几乎是标准/必需的?

4

3 回答 3

9

反正比赛是存在的。如果该文件可能存在也可能不存在,那么您必须在尝试锁定它之前测试它的存在。但是,如果该文件是您的互斥体,那么您就不可能这样做,并且“如果文件已存在”(假)和“下载到新创建的文件”之间的空间不受限制。另一个过程可能会在您开始下载之前创建文件并开始下载,您会破坏它。

这里基本上不使用fcntl锁,使用文件本身的存在。 open()如果文件已经存在,O_CREAT 和 O_EXCL 将失败,告诉您其他人先到达那里。

于 2013-06-27T20:45:53.327 回答
0

我目前正在努力解决类似的问题,这就是让我想到你的问题的原因。在我看来,本质是:

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);

也许所有这些讨论的底线是,以任何方式处理文件的每个进程都应该检查它是否为空并相应地执行。尽管如此,如果允许空文件,那么我还想不出任何有保证的解决方案。如果有某种方法可以创建文件并将其锁定为单个原子序列,那么这一切都不是必需的,但我认为没有任何方法可以做到这一点。

于 2018-06-21T20:02:01.343 回答
0

为什么不使用 lockfile 实用程序?

例子

假设您要确保对“重要”文件的访问是序列化的,即,不应允许超过一个程序或 shell 脚本访问它。为简单起见,我们假设它是一个 shell 脚本。在这种情况下,您可以像这样解决它:

...
lockfile important.lock
...
access_"important"_to_your_hearts_content
...
rm -f important.lock
...
于 2016-09-02T18:29:52.673 回答