今天ob在幾台DB上發現crontab中的監控任務,從來沒有執行。但操作一下crontab的配置<code>crontab –e + wq</code>,監控任務就能正常執行,重新開機cron也能正常執行。
出問題的機器crontab的情況如下:

問題挺奇怪,第一條crontab執行正常,第二條crontab一直沒有執行。
第一條正常,證明cron有正常運作。DB監控任務的crontab是凱麗系統自動安裝的,理論上不會存在突然出錯的情況。
cron出bug了? 第二個任務确實沒有執行,還是執行前遇到問題退出,剛好日志又列印到<code>/dev/null</code>了..
考慮到crontab執行曆史在系統日志裡會有記錄,檢查<code>var/log/message</code>日志如下,整<code>*/5</code>的分鐘點隻有crontab的第一個任務。
crontab第二個任務确實沒執行,應該是crontab出了點問題,再看看在修改crontab的那一刻發生了什麼?
根據crontab最後一次修改的時間(2012-06-18 17:51:01),檢查系統日志如下:
剛巧,在同一秒内修改了2次crontab。根據凱麗安裝監控的順序,第一個crontab應該是在安裝第一條crontab任務,第二個crontab應該是在安裝第二個crontab任務。
猜想:根據cron的工作原理,難道在增加第一個任務之後,cron加載了crontab的配置(<code>/var/spool/cron/tabs/mysql</code>),但是在增加第二個任務之後,cron沒有加載該配置? 如果是這樣,那麼cron判斷是否需要加載配置檔案的機制是如何的呢?
網上文檔稀少,直接看代碼。
在網上找到一個debian的cron源代碼: cron_3.0pl1.orig.tar.gz
解壓打開,檢視cron的執行過程:
在cron.c 的main函數中,cron的主函數代碼如下:
<code>cron_sleep()</code>: crontab 最小執行機關是分鐘,是以是每60秒執行一次.TargetTime += 60;
<code>load_database()</code>: 檢查crontab的配置檔案是否有更新,如果有更新,則重新load,否則使用上次記錄的database cron_tick( ): 實際執行任務檢視cron是如何加載crontab的配置檔案的,進入load_database()函數.
database.c :
可以看到,在cron加載配置的時候,會先擷取<code>/var/spool/cron/tabs</code> 目錄stat資訊,然後擷取使用者的crontab配置檔案的stat資訊,然後比較上一次統計的修改時間與tabs目錄、crontab配置檔案的最後修改時間,如果一緻則不重新load,否則重新load crontab配置檔案。
stat為系統函數調用,該函數取得的結構體的<code>st_mtime</code>的機關為秒。
至此,我們可以得出這樣的結論:
由于兩次crontab修改時間均在同一秒,而cron的加載是以crontab配置檔案的最後修改時間(秒級)來判斷檔案是否需要更新。當出現以下場景,第二次對crontab的修改就會失效(不僅是增加)。
crontab失效的場景:
而這個場景,剛好是我們的DB出現crontab失效的情況。
crontab的重新整理機制,是以crontab檔案的最後修改時間為準.
是以,如果在一秒内對crontab進行多次(大于1次)操作,就可能出現後修改的crontab不執行!
當再次對crontab檔案進行儲存操作時,cron會重新加載配置檔案,crontab生效.
該問題是由于在一秒内執行多次crontab變更導緻。是以解決辦法有3個:
在凱麗每次操作crontab的時候增加sleep 1的操作
在凱麗每次crontab操作完成之後,sleep 1,強制重新整理crontab的最後更新時間
合并并行的crontab操作為一次操作,減少對crontab的操作頻率
根據凱麗的情況,選擇第二個方案對現有代碼改動最小。
腳本盡量不要在同一秒内多次操作crontab内容,否則可能導緻crontab不生效的情況。