问题描述
我正在使用命令 flock
,它获取和释放文件锁定。例如,如果我运行
flock /tmp/mylock true
flock /tmp/mylock sleep 100
然后它会延迟 100 秒,再次获取和释放锁。而且,如果我在两个单独的 shell 中运行以下命令:
flock /tmp/mylock sleep 100
和
flock /tmp/mylock true
然后第二个命令被阻塞,因为它在第一个命令运行时无法获得锁。 sleep 100
完成并释放锁后,第二个命令将运行并退出。一切都很好。
问题来了。如果在这 100 秒的延迟期间,我在第三个 shell 中运行以下命令:
flock -u /tmp/mylock true
然后呢? flock 的手册页说:
-u,--unlock
Drop a lock. This is usually not required,since a lock is
automatically dropped when the file is closed. However,it may
be required in special cases,for example if the enclosed com-
mand group may have forked a background process which should not
be holding the lock.
所以,这应该解除锁定,这应该允许 flock /tmp/mylock true
运行,对吗? (我也猜测 flock /tmp/mylock sleep 100
会立即退出,但这是推测。)
会发生什么?没有什么。 flock -u /tmp/mylock true
立即退出,但 flock /tmp/mylock true
继续被阻止,而 flock /tmp/mylock sleep 100
继续退出。
flock -u /tmp/mylock <command>
实际上是做什么的?
(所有示例均在 Ubuntu 18.04 上测试。)
解决方法
这是一个示例,-u
使用文件描述符 9 打开文件 mylock
,成功解锁 9 以便后台 flock mylock
可以继续。
请注意,flock 9
不能也有命令,因为在这种情况下,“9”被视为文件名,而不是 fd。
bash -s <<\! 9>mylock 2>&1 |
flock 9; echo gotlock1
flock 9; echo gotlock2
9>&- flock mylock bash -c 'echo start_sleep;sleep 8; echo end_sleep' &
sleep 2
flock -u 9; echo unlock; sleep .1
flock 9; echo gotlock3
!
awk '{t2=systime(); if(t1==0)t1=t2; printf "%2d %s\n",t2-t1,$0; t1=t2}'
第一行让 bash 在打开 fd 9 后运行以下几行,但也通过最后看到的 awk 脚本管道 stdout 和 stderr。这只是为了用行的时间来注释输出。结果是:
0 gotlock1
0 gotlock2
2 unlock
0 start_sleep
8 end_sleep
0 gotlock3
这显示了前 2 个 flock 9
命令立即运行。然后 flock mylock
命令在后台运行,在关闭 fd 9 之后只为这一行。例如,该命令可以从第二个窗口运行。输出显示它挂起,因为我们没有看到 start_sleep
。这意味着前面的 flock 9
确实获得了排他锁。
然后输出显示,在 sleep 2
和 flock -u 9
之后,我们得到 unlock
回声,然后后台命令才获得锁并启动它的 sleep 8
。
主脚本立即执行 flock 9
,但输出显示直到后台脚本在 8 秒后以 end_sleep
结束,并且主脚本输出 gotlock3
后才会继续执行。
lslocks
命令有时会显示 2 个对锁感兴趣的进程。 *
表示等待:
COMMAND PID TYPE SIZE MODE M START END PATH
flock 23671 FLOCK 0B WRITE* 0 0 0 /tmp/mylock
flock 23655 FLOCK 0B WRITE 0 0 0 /tmp/mylock
但是它本身并没有显示第一个 flock 9
的结果,大概是因为没有带锁的进程,即使文件确实被锁定,正如我们在后台作业无法继续时看到的那样。