Linux 中陈旧文件锁的处理和 flock 的稳健使用

han*_*nno 4 shell-script lock flock

我有一个定期通过 cron 执行的脚本(每隔几分钟)。然而,脚本不应并行运行多次,有时运行时间会更长,因此我想实现一些锁定,即如果前一个实例已经在运行,请确保脚本提前终止。

根据各种建议,我有一个看起来像这样的锁定:

lock="/run/$(basename "$0").lock"
exec {fd}<>"$lock"
flock -n $fd || exit 1
Run Code Online (Sandbox Code Playgroud)

这应该调用 exit 1 以防脚本的另一个实例仍在运行。

现在问题来了:似乎有时即使脚本已经终止,陈旧的锁仍然存在。这实际上意味着 cron 永远不会再次执行(直到下次重新启动或删除锁定的文件),这当然不是我想要的。

我发现有列出现有文件锁的 lslocks 命令。它显示了这一点:

(unknown)        2732 FLOCK        WRITE 0     0   0 /run...                                                                 
Run Code Online (Sandbox Code Playgroud)

进程(在这种情况下为 2732)不再存在(例如在 ps aux 中)。我也不清楚为什么它不显示完整的文件名(即只有 /run...)。lslocks 有一个参数 --notrucate 在我看来它可能会避免截断文件名,但这不会改变输出,它仍然是 /run...

所以我有多个问题:

  • 为什么会有这些锁,什么情况会导致 flock 的锁存在于进程的生命周期之外?
  • 为什么 lslocks 不显示完整路径/文件名?
  • 什么是避免这种情况并使脚本中的锁定更健壮的好方法?
  • 有没有办法在不重新启动的情况下清理陈旧的锁?

the*_*ejh 5

一个flock锁与一个文件描述对象相关联; 一旦所有引用文件描述的文件描述符都关闭,它就会消失(参见flock.2 手册页)。

如果文件仍然被锁定,那么文件描述符几乎肯定仍然被原始进程或子进程引用(假设您没有使用文件描述符传递之类的东西在原始进程层次结构之外传播对它的引用) .

我会建议检查sudo fuser $lock_path

要解决这个问题,我知道有两种方法:要么阻止 shell 让子进程继承文件描述符,要么杀死所有仍在引用它的进程,例如使用fuser -k ....

您所看到的路径是不完整的,因为lslocks用于/proc/locks收集信息;此文件包含挂载点的标识符和有关获取锁定的进程的信息,但不包含锁定文件的路径。如果lslocks在检查该进程时找不到持有锁的文件描述符,它会回退到仅打印挂载点。