我有一个脚本,它运行一个程序,我只想让它每个脚本单独运行一个实例。例如,我有两个脚本“a”和“b”,它们分别运行一个 Java 程序。我只希望脚本“a”和脚本“b”分别运行一个 Java 程序实例。在这种情况下,我无法测试某个可执行文件是否正在运行,因为它是 JVM。启动应用程序后,脚本本身也会退出,所以我无法测试它是否已经在运行。
我可以将正在运行的进程的 PID 写入文件并进行测试,但这样做会引发竞争条件,因为我必须等待程序启动,然后再将 PID 记录到文件中。如果启动脚本运行多次且速度过快,最终会导致程序的多个实例同时运行。此外,还存在一种罕见的情况,即两个实例同时尝试写入 PID 文件,这会引发另一个问题,导致我有一个孤立进程,我根本没有 PID 记录。
什么是从启动脚本中锁定程序的可靠方法,以便我只能从脚本中运行单个实例而不存在竞争条件?
请注意,这并不仅限于运行 Java 程序。这只是一个例子。
假设这是一个典型的 Linux:这可以通过创建两个 systemd 服务单元来实现。脚本启动它们时会使用
无论脚本调用该函数的频率如何,该单元都仅启动一次。
对于 Bash 脚本,我锁定命令调用的方法是 Bash FAQ(链接在评论中)中第三个示例的变体。我使用了一个相当常见的锁定程序(
flock
),但我使用的方式不太常见。我将在示例代码后的描述中解释原因:这是特殊脚本的一部分,它将调用那些不能同时运行两个副本的命令。此脚本会获取锁文件(lockfile)上的锁,从而阻止重复的副本。如果锁成功,父脚本将调用敏感命令。如果锁失败,则不会调用这些命令。这被称为“协作锁”,因为当锁失败时,脚本会进行协作(不调用敏感命令)。
不常见的部分是用来
flock
锁定在锁文件上打开的文件描述符。这就是子shell存在的原因——它的文件描述符300在锁文件上打开,然后该shell运行该flock
命令。我使用这种方法是因为我的脚本无需执行任何特殊操作即可移除锁——当子shell退出时,文件描述符会关闭,锁也会自动释放。其他一些使用方法
flock
则要求脚本在敏感命令完成后显式解锁文件。这可能会带来问题,因为错误或崩溃可能会导致锁文件仍然处于锁定状态,脚本无法调用命令。这种文件描述符方法通常不会留下过期锁,因此脚本不需要额外的代码来解锁、检测过期锁并强制解锁等等。然而,凡事有利有弊。在某些情况下,命令执行失败后,最好保留锁,防止自动脚本再次运行该命令。您必须判断您的用例需要什么,并应用最佳解决方案。
谬误!你先以文件已存在时会失败的模式打开文件,然后写入任何内容(或者实际上什么都不写),然后只执行一次你想做的事情。(这涉及到生成一个新程序,这只是个“诱饵”;与问题无关。你需要 PID 做什么?你只关心你是否是唯一一个尝试运行该程序的人。)
在 bash 中,实现“仅当文件不存在时才创建文件,否则失败”的一个简单方法是