较早之前,一个模块的reload程序出现过这样一个问题:模块会定期检查一份文件是否被更新,如果被更新了,就reload;但是,如果文件正在被更新(没有写完)并且模块正好检测到更新,模块就会reload到一份不全的文件,导致数据异常。当时的解决方案是:使用一个done文件,推送文件的脚本在完成数据更新后,touch一下done,模块检测done文件的更新来判断是否reload。
最近这个事情又被同们事提起,不过稍微扩展了下,问题变成了:有没有一种机制,能让许多并行的脚本能串行进行一系列操作。比如多个脚本对文件进行依次读写,以免产生脏数据。无疑,如果脚本如果shell中有一种“锁”的机制,就可以解决这个问题。原来只使用过API级别的锁,脚本中的锁还真没用过。其实,如果要在shell脚本实现锁,需要满足两个条件:
- 一个全局可见的状态
- 一种“检测 + 加锁”的原子操作
大家可能会想到使用一个文件来当做锁,如果有这个文件,就表示某个脚本正在操作,其它脚本等待;如果没有这个文件,我就touch这个文件,然后开始我的操作。但是,“检测文件”和“新建文件”不是原子操作,所以是无法保证串行的。
google了一下,还是有几种满足条件的方案的。
文件夹锁
检测文件和新建文件无法做到原子性,但是mkdir操作,却能做到原子地检测文件夹和创建文件夹,有点儿意思!所以,当脚本想对于竞争数据进行操作,就mkdir
某个文件夹,根据返回码得知申请所是否成功,申请成功、完成操作之后再rm -rf
就可以实现了。例如这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
lockfile命令
原来直接就有个专门用于文件锁的命令,这个命令比mkdir更强大,可以设置申请文件锁的等待时长、重拾次数、锁的过期时间。但是,在写测试代码的时候,却发现只会有一个脚本一直获取到文件锁,其它脚本都处于申请锁等待并超时的状态,难道我参数用的不对?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
设置noclobber + 重定向文件
shell中有一个参数叫noclobber,设置了这个参数后,当脚本试图重定向文件时,如果发现改文件已经存在,重定向就会失败。这种方法自己没尝试过,下面是从网上抄来的code example:
1 2 3 4 5 6 7 8 |
|
看来在脚本中使用文件锁,还是比较方便的。不过,在我看来在脚本中使用文件锁,有个致命弱点:操作系统不会禁止其它进程对作为锁的文件(或者文件夹)进行操作。相当于一个脚本已经申请到了文件锁并正在操作,但是其它进程完全可以不受限制的删除这个文件锁,这样就会使得期间其它脚本能够成功申请到文件锁。或者一些脚本使用文件锁对竞争资源进行操作,但其它脚本直接操作竞争资源,这种情况也是无法避免的。使用文件锁,完全靠自觉!
另外,文件锁也没有提供像共享锁、排它锁这样的高级功能,这也是文件锁的短板。
参考资料:
http://en.wikipedia.org/wiki/File_locking
Lock your script (against parallel run)
—EOF—