首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何确保该脚本只运行一个自身的实例?

如何确保该脚本只运行一个自身的实例?
EN

Unix & Linux用户
提问于 2019-01-03 20:10:43
回答 2查看 3.9K关注 0票数 24

2013年8月19日,兰德尔·施瓦茨发布了 shell脚本,目的是确保在Linux上“只运行一个脚本实例,而不存在竞争条件,也不需要清理锁文件”:

代码语言:javascript
复制
#!/bin/sh
# randal_l_schwartz_001.sh
(
    if ! flock -n -x 0
    then
        echo "$ cannot get flock"
        exit 0
    fi
    echo "$ start"
    sleep 10 # for testing.  put the real task here
    echo "$ end"
) < $0

它似乎如宣传的那样起作用:

代码语言:javascript
复制
$ ./randal_l_schwartz_001.sh & ./randal_l_schwartz_001.sh
[1] 11863
11863 start
11864 cannot get flock
$ 11863 end

[1]+  Done                    ./randal_l_schwartz_001.sh
$

以下是我所理解的:

  • 脚本将自己内容的副本(即从<)重定向到子subshell的STDIN (即文件描述符0)。
  • 在子subshell中,脚本试图获得文件描述符0上的非阻塞独占锁(D12)。
    • 如果该尝试失败,子there就会退出(主脚本也会退出,因为它没有其他事情可做)。
    • 如果尝试成功,子subshell将运行所需的任务。

以下是我的问题:

  • 为什么脚本需要将它自己的内容的副本重定向到子subshell继承的文件描述符,而不是重定向其他文件的内容?(我尝试从另一个文件重定向并按上面的方式重新运行,执行顺序发生了变化:非后台任务在后台任务之前获得了锁。因此,也许使用文件本身的内容可以避免竞争条件,但如何避免?)
  • 为什么脚本需要将文件内容的副本重定向到子subshell继承的文件描述符?
  • 为什么在一个shell中保存文件描述符0的独占锁会阻止运行在不同shell中的同一个脚本的副本获得文件描述符0上的独占锁?shells是否有自己的标准文件描述符(012,即STDIN、STDOUT和STDERR)的独立副本?
EN

回答 2

Unix & Linux用户

发布于 2019-01-03 21:05:28

通过文件描述将文件锁附加到文件。在较高级别上,脚本的一个实例中的操作顺序是:

  1. 打开连接锁的文件(“锁定文件”)。
  2. 把锁在锁文件上。
  3. 做点什么。
  4. 关闭锁文件。这将释放附加到通过打开文件创建的文件描述的锁。

保持锁可以防止同一脚本的另一个副本运行,因为锁就是这样做的。

打开一个文件会创建一个文件描述。这是一个内核对象,在编程接口中没有太多的可见性。锁是文件描述的属性之一,而不是文件或描述符的属性。

在打开文件时,文件描述只有一个文件描述符,但可以通过创建另一个描述符( dup系列系统调用)或分叉子进程(其父进程和子进程都可以访问相同的文件描述)来创建更多的描述符。文件描述符可以显式关闭,也可以在进程退出时关闭。当附加到文件的最后一个文件描述符关闭时,文件描述将关闭。

下面是上面操作的顺序对文件描述的影响。

  1. 重定向<$0在子subshell中打开脚本文件,创建文件描述。此时,有一个文件描述符附加到描述:子subshell中的描述符号0。
  2. 子subshell调用flock并等待它退出。当flock正在运行时,在描述中附加了两个描述符:子subshell中的数字0和flock进程中的数字0。当flock获得锁时,它将设置文件描述的属性。如果另一个文件描述已经在该文件上有一个锁,flock不能接受该锁,因为它是一个独占锁。
  3. 潜水艇会起作用的。
  4. 子外壳在结束括号处死亡。这将关闭具有锁的文件描述上的最后一个文件描述符,因此锁在此时消失。

脚本使用来自$0的重定向的原因是,重定向是在shell中打开文件的唯一方法,而保持重定向活动是保持文件描述符打开的唯一方法。子subshell从不从它的标准输入中读取,它只需要打开它。在允许直接访问打开和关闭调用的语言中,可以使用

代码语言:javascript
复制
fd = open($0)
flock(fd, LOCK_EX)
do stuff
close(fd)

如果使用exec内置的重定向,实际上可以在shell中获得相同的操作序列。

代码语言:javascript
复制
exec <$0
flock -n -x 0
# do stuff
exec <&-

如果脚本想继续访问原始的标准输入,它可以使用不同的文件描述符。

代码语言:javascript
复制
exec 3<$0
flock -n -x 0
# do stuff
exec 3<&-

或者有一个子壳:

代码语言:javascript
复制
(
  flock -n -x 3
  # do stuff
) 3<$0

锁不一定在脚本文件上。它可以在任何可以打开以读取的文件上(因此它必须存在,它必须是可以读取的文件类型,例如常规文件或命名管道,而不是目录,并且脚本进程必须具有读取它的权限)。脚本文件的优点是它保证了它的存在和可读性(除了在调用脚本和脚本到达<$0重定向之间外部被删除的边缘情况下)。

只要flock成功,并且脚本位于锁没有错误的文件系统上(一些网络文件系统,如NFS可能有buggy),我就不知道如何使用不同的锁文件来允许争用条件。我怀疑你的操纵错误。

票数 10
EN

Unix & Linux用户

发布于 2019-01-03 20:49:21

用于锁定的文件并不重要,脚本使用$0,因为这是一个已知存在的文件。

获得锁的顺序或多或少是随机的,这取决于机器启动这两个任务的速度。

您可以使用任何文件描述符,而不一定是0。锁被保存在打开给文件描述符的文件上,而不是描述符本身。

代码语言:javascript
复制
( flock -x 9 || exit 1
  echo 'Locking for 5 secs'; sleep 5; echo 'Done' ) 9>/tmp/lock &
票数 4
EN
页面原文内容由Unix & Linux提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://unix.stackexchange.com/questions/492324

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档