创建锁定文件时防止竞争条件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建锁定文件时防止竞争条件相关的知识,希望对你有一定的参考价值。

我的脚本不能同时运行多次。因此它会创建一个锁定文件,并在退出之前将其删除。它在开始工作之前检查锁文件是否不存在。

一种非常常见的锁定方法是something like this

function setupLockFile() {
  if (set -o noclobber; echo "lock" > "$lockfile") 2>/dev/null; then
    trap "rm -f $lockfile; exit $?" INT TERM EXIT
  else
    echo "Script running... exiting!" 
    exit 1
  fi
}

但是存在竞争条件 - 如果文件不存在,if会创建文件,并且可以在定义trap之前终止脚本。然后不会删除锁定文件。

那么这样做的安全方法是什么?

答案

这不是一场竞赛 - 它对失败的抵御能力。如果脚本在删除文件之前死亡,则需要手动清理。

尝试自动执行此清理的常用方法是从任何现有文件中读取PID,测试以查看该进程是否仍然存在,并且如果不存在则基本上忽略它的存在。遗憾的是,没有原子比较和设置操作,这是非常简单的,因为它引入了一个新的种族,在读取PID和其他人试图忽略它的存在之间。

查看this question,了解有关仅使用文件系统进行锁定的更多建议。

我的建议是将锁文件存储在临时文件系统上(/var/run通常是tmpfs,以允许pidfiles在重新启动时安全地消失)以便在重新启动后自行修复,或者让脚本举手并请求手动干预。处理每个故障情况可靠地增加了复杂性,因此可能引入更多的失败概率而不是向人寻求帮助。

复杂性不仅仅是今天,而是代码的生命周期。当你完成它可能是正确的,但下一个人会打破它吗?

另一答案

让我们尝试另一种方法:

  • 在创建锁定文件之前设置陷阱
  • 将PID存储在锁定文件中
  • 使陷阱检查当前实例的PID是否与锁定文件中的任何内容匹配

例如:

trap "cleanUp" INT TERM EXIT

function cleanUp {
  if [[ $$ -eq $(<$lockfile) ]]; then
    rm -f $lockfile
    exit $?
  fi
}

function setupLockFile {
  if ! (set -o noclobber; echo "$$" > "$lockfile") 2>/dev/null; then
    echo "Script running... exiting!"
    exit 1
  fi
}

这样,您可以检查锁定文件是否存在,并将其创建为单个操作,同时还可以防止陷阱删除先前运行的实例的锁定文件。

另外,正如我在下面的评论中提到的,如果锁文件已经存在,我建议检查具有给定PID的进程是否正在运行。因为你永远都不知道锁文件是否仍然可以在磁盘上保持孤立状态。因此,如果您想减少手动删除孤立锁定fiels的需要,可以添加其他逻辑来检查PID是否是孤立的。

例如 - 如果没有找到来自锁定文件的给定PID的正在运行的进程,则可以假设这是来自先前实例的孤立锁定文件,并且您可以使用当前PID覆盖它并继续。如果找到进程,则可以比较其名称以查看它是否确实是同一脚本的另一个实例 - 如果不是,则可以覆盖锁定文件中的PID并继续。

我没有在代码中包含这个以保持简单,如果你愿意,你可以尝试自己创建这个逻辑。 :)

另一答案

首先检查lockfile,然后检查trap,然后写入:

function setupLockFile() {
  if [ -f "$lockfile" ]; then
    echo "Script running... exiting!" 
    exit 1
  else trap "rm -f $lockfile; exit $?" INT TERM EXIT
       set -o noclobber; echo "lock" > "$lockfile" || exit 1
  fi
}

并且有一种“官方”方法可以使用flock命令来检查lockfiles,这是util-linux的一部分。

以上是关于创建锁定文件时防止竞争条件的主要内容,如果未能解决你的问题,请参考以下文章

我是否必须锁定Blueprint实例以避免Flask中的竞争条件?

使用数据库事务防止竞争条件 (Laravel)

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

群():在没有竞争条件的情况下删除锁定的文件?

当发生写入数据库的并发 API 调用(或服务器速度较慢时)时,防止出现竞争条件