想到一個問題,如果在crontab里有個定時任務設置為一分鍾執行一次,但是它執行的時間可能會超過一分鍾,此時crontab一分鍾后會再次運行該腳本嗎?這樣會不會出現沖突呢?網上找了下,說可以用Linux中的進程鎖控制crontab執行的並發問題。
給一個shell腳本加鎖,使用flock命令。
一般格式:
flock [-sxun][-w #] fd#
flock [-sxon][-w #] file [-c] command...
常用選項:
-s, --shared :獲得一個共享的鎖。
-x, --exclusive :獲得一個獨占的鎖。
-u, --unlock :移除一個鎖,通常是不需要的,腳本執行完后會自動丟棄鎖。
-n, --nonblock :如果沒有立即獲得鎖直接失敗而不是等待。
-w, --timeout :如果沒有立即獲得鎖就等待指定的時間。
-o, --close :在運行命令前關閉文件的描述符。用於如果命令產生子進程時會不受鎖的管控。
-c, --command :在shell中運行一個單獨的命令。
-h, --help :顯示幫助。
-V, --version :顯示版本。
測試一下看看:
在/home目錄下建立一個test.sh。
vim /home/test.sh
輸入:
#!/bin/bash
wget --limit-rate=200k -P /tmp http://cachefly.cachefly.net/100mb.test
運行一個超過一分鍾的命令。
chmod +x /home/test.sh
編輯crontab:
crontab -e
輸入:
*/1 * * * * /usr/bin/flock -xn /var/run/test.lock -c '/home/test.sh'
設置每一分鍾執行一次。
重啟服務:
service crond restart
這樣只有第一個進程執行完畢后,才會執行當前的下一個進程。在第一個進程執行過程中,下一分鍾crontab運行flock檢測到獲得不了鎖,就直接退出,直到第一個進程執行完,flock再次獲得鎖。
注:
1、這種使用文件鎖的方式,在linux中非常常見,通過一個pid文件,來避免兩個進程同時運行,mysql和postgresql都有pid文件,mysql中只記錄了pid值,postgresql的pid中除了記錄有pid值,還有數據目錄,pid文件的創建時間,端口號,監聽地址和共享內存的地址。
2、使用PID文件鎖還有一個好處,方便進程向自己發停止或者重啟信號。Nginx編譯時可指定參數--pid-path=/var/run/nginx.pid,進程起來后就會把當前的PID寫入這個文件,當然如果這個文件已經存在了,也就是前一個進程還沒有退出,那么Nginx就不會重新啟動。進程管理工具Supervisord也是通過記錄進程的PID來停止或者拉起它監控的進程的
3、flock加的advisory lock,也就是建議性鎖,巧合的是,postgresql中也有advisory lock,非常適用於秒殺等高並發的場景。flock是通過獲取文件的fd來加鎖的。
參考:
http://www.live-in.org/archives/1036.html
https://linux.die.net/man/2/flock