1. 程式人生 > >Linux crond 不執行原因分析

Linux crond 不執行原因分析

為了定時監控Linux系統CPU、記憶體、負載的使用情況,寫了Linux Shell指令碼,當達到一定值得時候,定時傳送郵件通知。

但是,讓crond來週期性執行指令碼傳送郵件通知時,遇到了問題,在crontab -e裡面加入了執行指令碼之後,發現指令碼並沒有執行。

可是,通過手動執行Shell指令碼命令(./mimvp-email.sh)是正常的,因為手動執行指令碼可以預設獲取Linux的環境變數,但通過Crontab做的定時任務,則無法獲取環境變數。

分析了原因,crond不執行的原因主要有以下幾個方面:

1、crond服務沒啟動

ps -ef | grep -v grep | grep crond         // 檢視crond服務是否執行
service crond start           //關閉服務
service crond stop           //關閉服務
service crond restart       //重啟服務
service crond reload       //重新載入配置

2、使用者沒有執行crond的許可權

vim  /etc/cron.deny 檔案用來控制哪些使用者不能執行crond服務的功能。

可以將自己從檔案中刪去,或者聯絡root

3、crontab不提供所執行使用者的環境變數

解決方法:在指令碼中加入下面這一行:

. /etc/profile
. ~/.bash_profile

4、沒有使用絕對路徑

這裡的絕對路徑包括指令碼中的路徑和crond命令中的路徑兩個方面,例如:

*/10 * * * * sh /root/script/mysql_files_monitor.sh &

5、如果上面都沒有解決問題的話可以再找找問題:

1)去郵件看看,在這個過程中使用者應該會收到郵件,比如收到這樣的提示:

vim /var/spool/mail/root

You have mail in /var/spool/mail/root

去看看裡面就有crond的內容

檔案太大打不開,可以擷取最後1000行檢視

tail -n 1000 /var/spool/mail/root > aaa.txt  &&  vim aaa.txt

2)在腳本里面加入output用來除錯

可以在crontab的腳本里面添加個

echo $PATH > /tmp/test.log

對比和終端下執行指令碼的echo $PATH

6、crond程序太多,全部殺死重啟crond服務

#!/bin/bash
for i in $(
ps -elf | grep -v grep | grep crond | awk -F " " '{print $4}'

 ); do
     kill -9  $i
done

使用root執行重啟,後問題解決:

service crond restart

7、crond防止腳本週期內未執行完重複執行

個人體會: flock -xn my.lock cmd
my.lock是一個檔案,可以是任意檔案,可以新建一個空檔案
當flock 獲得鎖後就會執行後面的 cmd

測試過程:

$1: flock -xn my.lock sleep 20
$2: flock -xn my.lock ls

只有當1返回後, 2的ls才會成功

如果某指令碼要執行30分鐘,可以在Crontab裡把指令碼間隔設為至少一小時來避免衝突。而比較糟的情況是可能該指令碼在執行週期內沒有完成,接著第 二個指令碼又開始運行了。如何確保只有一個指令碼例項執行呢?一個好用的方法是利用lockf(FreeBSD 8.1下為lockf,CentOS  5.5下為flock),在指令碼執行前先檢測能否獲取某個檔案鎖,以防止指令碼執行衝突。

lockf 引數如下

-k:一直等待獲取檔案鎖。

-s:silent,不發出任何資訊,即使拿不到檔案鎖。

-t seconds:設定timeout的時間是seconds秒,如果超過時間,則自動放棄。

以下crontab計劃任務執行前,需獲取臨時檔案create.lock 檔案鎖,crontab計劃任務的內容如下:

1 */10 * * * * (lockf -s -t 0 /tmp/create.lock /usr/bin/python /home/project/cron/create_tab.py  >> /home/project/logs/create.log 2>&1)

若第一個例項在10分鐘內沒有執行完,第2個例項不會執行。我以前是通過Shell指令碼來解決這個問題的,比如用while...do迴圈,然後放在後臺執行。但後來發現其實用flock或lockf方法更為簡單。

附上linux下的flock的用法:

flock (util-linux 2.13-pre7)
Usage: flock [-sxun][-w #] fd#
       flock [-sxon][-w #] file [-c] command...
  -s  --shared     Get a shared lock 
#共享鎖,在定向為某檔案的FD上設定共享鎖而未釋放鎖的時間內,其他程序試圖在定向為此檔案的FD上設定獨佔鎖的請求失敗,而其他程序試圖在定向為此檔案的FD上設定共享鎖的請求會成功
  -x  --exclusive  Get an exclusive lock 
#獨佔或排他鎖,在定向為某檔案的FD上設定獨佔鎖而未釋放鎖的時間內,其他程序試圖在定向為此檔案的FD上設定共享鎖或獨佔鎖都會失敗。只要未設定-s引數,此引數預設被設定
  -u  --unlock     Remove a lock 
#手動解鎖,一般情況不必須,當FD關閉時,系統會自動解鎖,此引數用於指令碼命令一部分需要非同步執行,一部分可以同步執行的情況
  -n  --nonblock   Fail rather than wait 
#為非阻塞模式,當試圖設定鎖失敗,採用非阻塞模式,直接返回1,
  -w  --timeout    Wait for a limited amount of time
#設定阻塞超時,當超過設定的秒數,就跳出阻塞,返回1
  -o  --close      Close file descriptor before running command
  -c  --command    Run a single command string through the shell 執行其後的comand
  -h  --help       Display this text
  -V  --version    Display version
舉個例子執行如下指令碼:

每天23:30的時候執行一個指令碼,但是執行前必須要獲得排他檔案鎖,否則無法執行命令

1 30 23 * * * flock -xn /tmp/test.lock -c '/usr/local/php test.php'

8、; 和 && 區別

“;” 和 “&&”是有區別的

“;”:不管cmd1執行的結果如何,都執行cmd2

“&&”:只有cmd1執行返回的結果是成功的,才執行cmd2

cmd1 && cmd2; cmd3

- cmd1 is executed, if it succeeds, then execute cmd2. and then cmd3 (regardless of cmd2 success or not)

- cmd1 is executed, if it fails, then cmd3 (cmd2 won't be executed)

9、如果遇到shell語法錯誤

Syntax error: "(" unexpected  

解決方法:

需指定shell直譯器命令:SHELL=/bin/bash(請參見上面 crontab編輯示例 SHELL=/bin/bash

如果遇到路徑錯誤

在 /var/spool/crontab/yanggang 中,添加了如下命令,在日誌檔案 /var/spool/mail/yanggang 中提示找不到 xxx.sh 路徑

30 * * * *  /home/barry/top800/top10/top10_fruits/top10_all.sh

30 * * * * bash /home/barry/top800/top10/top10_fruits/top10_all.sh

這是因為你在crontab中使用了絕對路徑執行指令碼 top10_all.sh,因此在指令碼 top10_all.sh 中引用的其它指令碼也都需要使用絕對路徑,才能被crontab找到並執行。

那麼該如何避免絕對路徑呢,推薦採用如下格式:

30 * * * * cd /home/barry/top800/top10/top10_fruits/ && ./top10_all.sh推薦用此方式

先進入該目錄,然後在執行指令碼;否則,執行指令碼中的其它指令碼都需要加絕對路徑

參考推薦: