创建锁定文件时防止竞争状态

时间:2018-09-11 11:32:23

标签: linux bash shell locking

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

一种非常常见的锁定方法是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之前终止脚本。这样,锁定文件将不会被删除。

那么安全的方法是什么?

3 个答案:

答案 0 :(得分:3)

那不是比赛-它是失败的韧性。如果脚本在删除文件之前死掉,则需要手动清理。

尝试自动执行此清除操作的通常方法是从任何现有文件中读取PID,进行测试以查看该进程是否仍然存在,如果不存在,则基本上忽略其存在。不幸的是,由于没有原子的比较和设置操作,要进行正确的操作并非易事,因为它引入了新的竞争,在读取PID和其他试图忽略其存在的人之间。

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

我的建议是将锁定文件存储在临时文件系统中(/var/run通常是tmpfs,以允许pidfile在重新启动时安全消失),以便在重新启动后自行修复,或者让脚本抛出该错误。双手并要求人工干预。可靠地处理每一个失败案例都会增加复杂性,因此可能比寻求人工帮助带来更多的失败可能性。

复杂性不仅限于今天,还涉及代码的生命周期。完成后可能是正确的,但是下一个人会破坏它吗?

答案 1 :(得分:1)

让我们尝试另一种方法:

  • 在创建锁定文件之前设置陷阱
  • 将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的进程是否正在运行。 因为您永远不知道由于某种原因,锁定文件仍然可以在磁盘上保持孤立状态。 因此,如果您希望减轻手动删除孤立锁线的需求,则可以添加其他逻辑来检查PID是否为孤立的。

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

为了简化起见,我没有在代码中包括它,如果需要,您可以尝试自己创建此逻辑。 :)

答案 2 :(得分:0)

首先检查锁文件,然后陷阱,然后将其写入:

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命令来检查锁文件,该命令是util-linux的一部分。

相关问题