【问题标题】:How do I atomically create a locked file in Linux?如何在 Linux 中原子地创建锁定文件?
【发布时间】:2013-06-25 12:22:28
【问题描述】:

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

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

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 方法几乎是标准/必需的?

【问题讨论】:

    标签: linux file-io flock fcntl


    【解决方案1】:

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

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

    【讨论】:

    • 如果我只能在文件不存在的情况下自动创建锁定文件,我不确定比赛将如何存在。 (条件也是原子的)。 O_CREAT |如果文件不存在,O_EXCL 是原子创建;我只想用锁来做到这一点。另一个进程无法开始下载,因为它会检测到文件存在。最后,我不能单独使用 open ,因为我必须阻止直到下载完成;在我的解决方案中,我依赖于(独占读/写)锁。
    • 你是对的,原子地创建和锁定文件可以解决你的问题。但这不是 fcntl 锁的工作方式,抱歉。如果您想使用 fcntl 锁,则您要锁定的文件必须在您的程序执行任何同步操作之前存在。找到一种方法将创建移到伪代码中的 if() 之外,或者制定另一个不使用下载文件作为互斥锁的同步协议。
    • 知道了,所以答案是 Linux 不提供带有打开功能的 Windows 风格的原子锁。我会坚持使用 lockfile 方法。
    【解决方案2】:

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

    例子

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

    ...
    lockfile important.lock
    ...
    access_"important"_to_your_hearts_content
    ...
    rm -f important.lock
    ...
    

    【讨论】:

      【解决方案3】:

      我目前正在努力解决类似的问题,这就是 带我回答你的问题。在我看来,本质是:

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

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

      【讨论】:

      • 一个小点:可以锁定父目录本身。只需像任何其他文件一样以只读方式打开目录并获得该句柄的排他锁。只要每个人都遵守规则,您现在可以在拥有锁的情况下随意创建和删除文件。缺点是锁定父目录的粒度小于锁定单个文件。
      猜你喜欢
      • 1970-01-01
      • 2011-07-09
      • 1970-01-01
      • 2014-12-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-13
      相关资源
      最近更新 更多