在開發核心模組或驅動時,如果處理失誤,導致核心線程中出現死結或者死迴圈,你會發現,除了重啟之外,你沒有任何可以做的。這時你的輸入不起任何作用,終端(不是指遠端ssh工具)只會在那重複的輸出類似“BUG: soft lockup - CPU#0 stuck for 67s! [fclustertool:2043]”,更無奈的是你重啟之後導致系統掛起的堆棧資訊也看不到,你所能做的就是一遍遍的加調試資訊,一遍遍的重啟機器(這是我的經曆,現在想想很傻)。
這種情況你肯定不是第一個遇到的,所以核心肯定會提供處理這種情況的一些機制。但是如何來找到這些機制在哪個地方,或者說根據什麼資訊去google呢?最有用的就是這句話“BUG: soft lockup - CPU#0 stuck for 67s! [fclustertool:2043]”,因為這句話提供你的資訊量很大。首先,這條資訊可以輸出,說明即使發生死結或者死迴圈,還是有代碼可以執行。第二,可以通過這個日誌資訊,找到對應的處理函數,這個函數所在的模組就是用來處理CPU被過度使用時用到的。所以通過這個事情,可以看到核心列印出的隻言片語都有可能成為你解決問題的關鍵,一定要從重視這些資訊,從中找出有用的東西。
我經常看的核心版本是官方的2.6.32核心,這個版本中我找到的函數是softlockup_tick(),這個函數在時鐘中斷的處理函數run_local_timers()中調用。這個函數會首先檢查watchdog線程是否被掛起,如果不是watchdog線程,會檢查當前佔有CPU的線程佔有的時間是否超過系統配置的閾值,即softlockup_thresh。如果當前佔有CPU的時間過長,則會在系統日誌中輸出我們上面看到的那條日誌。接下來才是最關鍵的,就是輸出模組資訊、寄存器資訊和堆棧資訊,檢查softlockup_panic的值是否為1。如果softlockup_panic為1,則調用panic()讓核心掛起,輸出OOPS資訊。代碼如下所示:
/** This callback runs from the timer interrupt, and checks* whether the watchdog thread has hung or not:*/void softlockup_tick(void){ int this_cpu = smp_processor_id(); unsigned long touch_timestamp = per_cpu(touch_timestamp, this_cpu); unsigned long print_timestamp; struct pt_regs *regs = get_irq_regs(); unsigned long now; ...... /* Warn about unreasonable delays: */ if (now <= (touch_timestamp + softlockup_thresh)) return; per_cpu(print_timestamp, this_cpu) = touch_timestamp; spin_lock(&print_lock); printk(KERN_ERR "BUG: soft lockup - CPU#%d stuck for %lus! [%s:%d]\n", this_cpu, now - touch_timestamp, current->comm, task_pid_nr(current)); print_modules(); print_irqtrace_events(current); if (regs) show_regs(regs); else dump_stack(); spin_unlock(&print_lock); if (softlockup_panic) panic("softlockup: hung tasks");}
但是softlockup_panic的值預設竟然是0,所以在出現死結或者死迴圈的時候,會一直只輸出日誌資訊,而不會宕機,這個真是好坑啊!所以你得手動修改/proc/sys/kernel/softlockup_panic的值,讓核心可以在死結或者死迴圈的時候可以宕機。如果你的機器中安裝了kdump,在重啟之後,你會得到一份核心的core檔案,這時從core檔案中尋找問題就方便很多了,而且再也不用手動重啟機器了。如果你的核心是標準核心的話,可以通過修改/proc/sys/kernel/softlockup_thresh來修改逾時的閾值,如果是CentOS核心的話,對應的檔案是/proc/sys/kernel/watchdog_thresh。CentOS核心和標準核心還有一個地方不一樣,就是處理CPU佔用時間過長的函數,CentOS下是watchdog_timer_fn()函數。
這裡介紹下lockup的概念。lockup分為soft lockup和hard lockup。 soft lockup是指核心中有BUG導致在核心模式下一直迴圈的時間超過10s(根據實現和配置有所不同),而其他進程得不到啟動並執行機會。hard softlockup是指核心已經掛起,可以通過watchdog這樣的機制來擷取詳細資料。這兩個概念比較類似。如果你想瞭解更多關於lockup的資訊,可以參考這篇文檔:
http://www.mjmwired.net/kernel/Documentation/lockup-watchdogs.txt。
注意上面說的這些,都是在核心線程中有效,對使用者態的死迴圈沒用。如果要監視使用者態的死迴圈,或者記憶體不足等資源的情況,強烈推薦軟體層面的watchdog。你可以自己開發基於soft watchdog的監控程式,也可以安裝watchdog包通過修改配置來做,非常的方便。具體的操作可以參考下面的文章,都寫的非常好,非常實用:
http://purplegrape.blog.51cto.com/1330104/1131910
http://www.ibm.com/developerworks/cn/linux/l-cn-watchdog/index.html#resources