我正在为备份任务编写一个 bash 脚本。除其他事项(文件系统冻结、数据库锁定、快照等)外,它还涉及关闭 Apache 并在备份后重新启动它。由于该脚本应作为 cronjob 运行,并且执行时间可能相差很大(特别是因为脚本等待“好时机”进行备份),我尝试使用 来保护它免于多次执行flock
。
但是,flock
即使备份脚本退出后,仍会保持锁定。此行为与我的使用方式无关flock
(使用脚本中打开的目录、文件或文件描述符)。
我把问题归结为 apache2 的重新启动,并可以在以下一行中看到它
flock -n /var/lock/startapache service apache2 start
请参阅以下交互式会话来说明该问题:
root@fermat:/home/ubuntu# service apache2 stop
* Stopping web server apache2 ... waiting . [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
* Starting web server apache2 [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
failed
这是因为启动的 Apache 脚本保持我的锁定文件描述符处于打开状态:
root@fermat:/home/ubuntu# lsof /var/lock/startapache
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
apache2 23651 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23656 trac 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23674 trac 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23675 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23676 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23677 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23694 root 3u REG 0,17 0 6673997 /run/lock/startapache
apache2 23696 root 3u REG 0,17 0 6673997 /run/lock/startapache
一旦我关闭 Apache,锁就会再次被释放:
root@fermat:/home/ubuntu# service apache2 stop
* Stopping web server apache2 ... waiting [ OK ]
root@fermat:/home/ubuntu# flock -n /var/lock/startapache service apache2 start || echo failed
* Starting web server apache2
因此我的问题是:Apache 进程如何“继承”这些文件描述符?为什么其他启动脚本(例如“service mysql start”)不会发生相同的行为?有没有办法避免这种情况?
答案1
由于没有人写答案(尽管有赏金),我将自己解释一下我最终是如何解决这个问题的。
穆鲁的评论(非常感谢!)是正确的,并让我走上了正确的轨道:在 Ubuntu 14.04 中,Apache 启动由 sysv init 脚本管理,作为 shell 脚本,它继承了所有内容(环境变量、打开的文件描述符等),并且由它启动的 apache 进程也将继承所有这些。而 Mysql 由 Upstart 管理,这确保了启动服务的环境干净。这解释了 Mysql 和 Apache 的不同行为。
有了这些知识,我更清楚要寻找什么,并且找到了unix.stackexchange.com 上的这个答案。建议在调用不应继承它的脚本的子 shell 中关闭文件描述符,同时在外壳程序中保持描述符打开,以确保锁保持有效。
不幸的是,这意味着我必须将flock
调用移到 shell 脚本中,否则我的脚本将不知道要关闭哪个文件描述符。所以我的新 shell 脚本(也从中汲取了一些灵感)这篇博客文章flock
) 的结构如下:
#!/bin/bash
FLOCK_FILE="/var/lock/backup-lock"
FLOCK_FD=20
# Locking
eval "exec $FLOCK_FD>'$FLOCK_FILE'"
if ! flock -n $FLOCK_FD
then
echo "FAILED! There is a backup script already running."
exit 1
fi
(
# Unlock in sub-shell, so daemons with bad startup scripts
# (like Apache) don't inherit the look.
# Note that the lock is still alive in general because it's
# held by the outer shell.
eval "exec $FLOCK_FD>-"
# ... normal backup stuff from the original script ...
# Among other stuff the mentioned vicious line:
service apache2 start
# ... normal backup stuff from the original script ...
)
# Unlock in outer shell because we're done.
eval "exec $FLOCK_FD>-"