shell脚本文件锁——避免脚本重复执行

1.在shell中实现文件锁的两种方式

  • 方法一:利用普通文件
    (1)在脚本启动时检查特定文件是否存在,如果存在,则等待一段时间后继续检查,直到文件不存时创建该文件,在脚本结束时删除文件。
    (2)为确保脚本在异常退出时文件仍然能被删除,可以借助于trap “cmd” EXIT TERM INT命令。
    (3)一般这类文件存放在/var/lock/目录下,操作系统在启动时会对该目录做清理。

  • 方法二:使用flock命令
    (1)使用方式如下,这个命令的好处是等待动作在flock命令中完成,无需另外添加代码。
    ( flock 300 …cmd… flock -u 300 ) > /tmp/file.lock
    (2)但flock有个缺陷是,在打开flock之后fork(),子进程也会拥有锁,如果在flock其间有运行daemon的话,必需确保daemon在启动时已经关闭了所有的文件句柄,不然该文件会因为daemon一直将其置于打开状态而无法解锁。

2.使用flock命令

  • flock 是对于整个文件的建议性锁。
    (1)也就是说,如果一个进程在一个文件(inode)上放了锁,那么其它进程是可以知道的。(建议性锁不强求进程遵守。)
    (2)最棒的一点是,它的第一个参数是文件描述符,在此文件描述符关闭时,锁会自动释放。
    (3)而当进程终止时,所有的文件描述符均会被关闭。
  • shell中实现flock系统调用的命令是flock,其格式为:
flock [-sxon] [-w timeout] lockfile [-c] command...
flock [-sxun] [-w timeout] fd

-s,--shared:获取一个共享锁,在定向为某文件的FD上设置共享锁而未释放锁的时间内,
其他进程试图在定向为此文件的FD上设置独占锁的请求失败,而其他进程试图在定向为此文件的FD上设
置共享锁的请求会成功。

-x,-e,--exclusive:获取一个排它锁,或者称为写入锁,为默认项

-u,--unlock:手动释放锁,一般情况不必须,当FD关闭时,系统会自动解锁,此参数用于脚本命令一部分需要异步执行,一
部分可以同步执行的情况。

-n,--nb, --nonblock:非阻塞模式,当获取锁失败时,返回1而不是等待

-w, --wait, --timeout seconds:设置阻塞超时,当超过设置的秒数时,退出阻塞模式,返回1,并继续执行后面的语句

-o, --close:表示当执行command前关闭设置锁的FD,以使command的子进程不保持锁。

-c, --command command:在shell中执行其后的语句
  • eg:Linux中的例行性工作排程crontab会定时执行一些脚本,但脚本的执行时间往往无法控制,当脚本执行时间过长时,可能会导致上一次任务的脚本还没执行完,下一次任务的脚本又开始执行了。
    这种情况下可能会出现一些并发问题,严重时会导致出现脏数据/性能瓶颈的恶性循环。
    通过使用flock建立排它锁可以规避这个问题
==================测试脚本如下:cat /scripts/shell/file_lock.sh======================


#cat /scripts/shell/file_lock.sh

#!/bin/bash

# Description: test for file flock

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH
echo ""

echo "----------------------------------"
echo "start at `date '+%Y-%m-%d %H:%M:%S'` ..."
sleep 140s
echo "finished at `date '+%Y-%m-%d %H:%M:%S'` ..."


=====================创建定时任务:测试排它锁=========================================
#crontab -e
* * * * * flock -xn /dev/shm/test.lock -c "sh /scripts/shell/file_lock.sh > /root/stdout.log"
##每分钟执行一次该脚本,并将输出信息写入到stdout.log

==========================查看输出日志如下:==========================================
----------------------------------

start at 2014-04-10 10:23:01 ...            #获取锁

finish at 2014-04-10 10:25:21 ...           #释放锁
---------------------------------

start at 2014-04-10 10:26:01 ...            #10:27:0010:28:00启动的定时任务由于无法获取锁,以失败而退出执行,直到10:26:00才获取到锁

finish at 2014-04-10 10:28:21 ...

==========================测试排它锁,加上等待超时=======================================

* * * * * flock -x -w 20 /dev/shm/test.lock -c "sh /scripts/shell/file_lock.sh > /root/stdout.log"
* 
==========================查看输出日志如下:=============================================
---------------------------------

start at 2014-04-10 10:29:01 ...

finish at 2014-04-10 10:31:21 ...
----------------------------------

start at 2014-04-10 10:31:21 ...    #10:31:00启动的定时任务等待了20秒后,上一个任务释放了锁,所以此任务可以马上拿到锁,并继续执行

finish at 2014-04-10 10:33:41 ...

3.Linux shell文件锁

  • eg:如何能不让脚本重复执行的问题,实际就是文件锁的概念
#!/bin/bash
LockFile()
{
 find/dev/shm/* -maxdepth 0 -type l -follow -exec unlink {} \;
 [ -f /dev/shm/${0##*/}]&&exit
 ln -s /proc/$$/dev/shm/${0##*/}
 trap "Exit" 0 1 2 3 15 22 24
}
Exit()
{
 unlink /dev/shm/${0##*/};
 exit 0;
}
LockFile
# main program
# program ......
#Exit
  • /var/lock/subsys目录的作用的说明
    (1)很多程序需要判断是否当前已经有一个实例在运行,这个目录就是让程序判断是否有实例运行的标志,比如说xinetd,如果存在这个文件,表示已经有xinetd在运行了,否则就是没有,当然程序里面还要有相应的判断措施来真正确定是否有实例在运行。
    (2)通常与该目录配套的还有/var/run目录,用来存放对应实例的PID,如果你写脚本的话,会发现这2个目录结合起来可以很方便的判断出许多服务是否在运行,运行的相关信息等等。

  • 缺点:实际上,判断是否上锁就是判断这个文件,所以文件存在与否也就隐含了是否上锁。
    而这个目录的内容并不能表示一定上锁了,因为很多服务在启动脚本里用touch来创建这个加锁文件,**在系统结束时该脚本负责清除锁,这本身就不可靠(比如意外失败导致锁文件仍然存在),**我在脚本里一般是结合PID文件(如果有PID文件的话),从PID文件里得到该实例的PID,然后用ps测试是否存在该PID,从而判断是否真正有这个实例在运行,更加稳妥的方法是用进程通讯了,不过这样的话单单靠脚本就做不到了。

  • 参考:shell脚本实现文件锁功能linux shell之文件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章