题 防止重复的cron作业运行


我计划每分钟运行一个cron作业,但有时脚本需要一分多钟才能完成,我不希望这些作业开始“堆叠”在一起。我想这是一个并发问题 - 即脚本执行需要互斥。

为了解决这个问题,我让脚本寻找特定文件的存在(“lockfile.txt“)并退出,如果它存在或 touch 它如果没有。但这是一个非常糟糕的信号量!我应该知道最佳做法吗?我应该写一个守护进程吗?


81
2017-11-09 11:32






答案:


有几个程序可以自动执行此功能,消除了自己做的烦恼和潜在的错误,并通过在幕后使用flock来避免过时的锁定问题(如果你只是使用触摸就会有风险) 。我用过 lockrun 和 lckdo 在过去,但现在有 flock(1)(在新版本的util-linux中)非常棒。它真的很容易使用:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

109
2017-11-09 11:57



lckdo将从moreutils中删除,现在flock(1)在util-linux中。并且该软件包在Linux系统中基本上是强制性的,因此您应该能够依赖它的存在。使用方法请参见下文。 - jldugger
是的,羊群现在是我的首选。我甚至会更新我的答案。 - womble♦
有谁知道它们之间的区别 flock -n file command 和 flock -n file -c command ? - Nanne
@Nanne,我必须检查代码以确定,但我的教育猜测是 -c 通过shell运行指定的命令(根据联机帮助页),而“裸”(非)-c形成刚才 exec给出了命令。通过shell放置一些东西可以让你做类似shell的事情(例如运行多个命令分隔 ; 要么 &&),但如果您使用不受信任的输入,也会打开shell扩展攻击。 - womble♦
这是(假设的)的一个论据 frequent_cron_job 试图显示它的命令每分钟都在运行。我删除它,因为它没有添加任何有用的东西,并造成混乱(你的,多年来没有其他人)。 - womble♦


shell的最佳方式是使用 涌向(1)

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

27
2017-11-09 11:45



我不能不投票使用fd重定向。这真是太棒了。 - womble♦
在Bash或ZSH中不解析我,需要消除之间的空间 99 和 > 所以它是 99> /... - Kyle Brandt♦
@Javier:这并不意味着它不是棘手和晦涩的,只是它是 记录,棘手和神秘。 - womble♦
如果你在运行时重新启动或者以某种方式杀死进程会发生什么?它会被永远锁定吗? - Alex R
我理解这个结构创建了一个独占锁,但我不明白这是如何完成的机制。在这个答案中'99'的功能是什么?有人在乎解释这个吗?谢谢! - Asciiom


其实, flock -n 可以用来代替 lckdo*,因此您将使用内核开发人员的代码。

建立在 womble的例子,你会写像:

* * * * * flock -n /some/lockfile command_to_run_every_minute

BTW,查看代码,全部 flocklockrun,和 lckdo 做同样的事情,所以这是最容易获得的问题。

*由于我在撰写本文时的声誉,我既不能编辑也不能评论以前的答案,我必须将其作为一个单独的答案。


21
2017-11-19 22:43





您可以使用锁定文件。脚本启动时创建此文件,完成后将其删除。该脚本在运行其主例程之前,应检查锁定文件是否存在并相应地继续。

锁文件由initscripts和Unix系统中的许多其他应用程序和实用程序使用。


2
2017-11-09 11:36



这是 只要 我亲眼见过它实现的方式。我根据维护者的建议使用on作为OSS项目的镜像 - warren


这也可能表明你做错了。如果您的工作经常紧密地运行,那么也许您应该考虑对其进行分解并使其成为守护程序式程序。


1
2017-11-09 11:45



我衷心地不同意这一点。如果你有需要定期运行的东西,那么把它变成一个守护进程是一个“坚固的大锤”解决方案。使用锁定文件来防止事故是一个非常合理的解决方案,我从来没有遇到过使用过的问题。 - womble♦
@womble我同意;但我喜欢用大锤砸碎坚果! :-) - wzzrd


您尚未指定是否希望脚本等待上一次运行完成。通过“我不希望工作开始”堆叠“相互叠加”,我想你暗示你希望脚本在已经运行时退出,

所以,如果你不想依赖lckdo或类似的东西,你可以这样做:


PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work


1
2017-11-09 14:33



谢谢您的示例很有用 - 我确实希望脚本在已经运行时退出。谢谢你的提及 ickdo  - 它似乎做了伎俩。 - Tom


如果它们的先前实例仍在运行,则您的cron守护程序不应该调用作业。我是一个cron守护进程的开发者 dcron,我们特别试图阻止这一点。我不知道Vixie cron或其他守护进程如何处理这个问题。


1
2018-02-17 15:59