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之文件

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