如何製造一個內核態的軟鎖死?

事情是這樣的,最近在2.6.32內核的centos下經常遇到設備卡死、能ping到、但無法遠程登錄。重啟後查看系統message,發現kernel報softlockup。

暫時知道是由某個應用造成的,但是升級的工程浩大,所以想要一個臨時方案……

上網找了一些資料,說只要修改/proc/sys/kernel/softlockup_panic為1,系統就會在softlockup後hung tasks並重啟。可是怎麼測試有效呢,所以有了如題的想法。

現在暫時只能等情況再現,然觀察了幾天後發現並卵啊……

望有這方面經驗的大神賜教。


softlockup 是指系統的調度器出現了問題,導致進程無法調度,使系統呈現「軟 hung 死的狀態」(CPU 上還是有程序在跑著,就是沒法讓別人來搶佔它了而已,所以用戶看起來系統不能切換任務了,像 hung 死了一樣).

內核如何檢測這種情況發生?

內核的做法呢是每個 CPU 上有一個實時線程,它會被周期地調度. 這個線程的工作就是寫一個變數,其實就是把當前 CPU 上維護的一個時間戳寫進去.假如該 CPU 上的調度沒有問題,那麼這個變數的值是會非常新的.假如不是,比如調度器有好久沒在這個 CPU 上切換任務了,那麼這個時間戳值就會比較舊了.那系統是如何判斷出這種異常情況呢?繼續看。

有一個時鐘,它也會周期地被喚醒,時鐘處理函數,會去檢測這個變數值,假如它發現這個變數值,也就是這個時間戳,距離當前最新時間的間隔已經超過了 softlockup 允許的最大時間值(默認是20s,可以設置),它就會報警,如果你設置了 /proc/sys/kernel/softlockup_panic 為1,那麼它就讓系統 panic.注意,時鐘依賴中斷機制,而中斷機制不依賴調度器,所以可以用它來監控調度器工作與否。

Ok, 知道了原理,但 softlockup 是為了檢測內核潛在的 bug 的,要等到他出現並不容易.但是我們可以採取 hacky 的方法,人為注入 softlockup 事件。方法如下:

樓主,你先下載一個 crash 工具到那個系統上. crash 這個工具一般是用來分析 linux kernel crash之後的 coredump 的.但它也可以被用來當實時的調試器,比如修改內核變數.

下面看我操作,怎麼用 hacky 方法來構造此事件.

1.從內核源碼中,找到上述那個變數是 watchdog_touch_ts, 我們把它列印出來,看是什麼值.

crash&> p watchdog_touch_ts

PER-CPU DATA TYPE:

long unsigned int watchdog_touch_ts;

PER-CPU ADDRESSES:

[0]: ffff88203fc0d428

[1]: ffff88203fc2d428

[2]: ffff88203fc4d428

[3]: ffff88203fc6d428

[4]: ffff88203fc8d428

[5]: ffff88203fcad428

[6]: ffff88203fccd428

[7]: ffff88203fced428

[8]: ffff88403fc0d428

[9]: ffff88403fc2d428

[10]: ffff88403fc4d428

[11]: ffff88403fc6d428

[12]: ffff88403fc8d428

[13]: ffff88403fcad428

[14]: ffff88403fccd428

[15]: ffff88403fced428

[16]: ffff88203fd0d428

[17]: ffff88203fd2d428

[18]: ffff88203fd4d428

[19]: ffff88203fd6d428

[20]: ffff88203fd8d428

[21]: ffff88203fdad428

[22]: ffff88203fdcd428

[23]: ffff88203fded428

[24]: ffff88403fd0d428

[25]: ffff88403fd2d428

[26]: ffff88403fd4d428

[27]: ffff88403fd6d428

[28]: ffff88403fd8d428

[29]: ffff88403fdad428

[30]: ffff88403fdcd428

[31]: ffff88403fded428

這是個每CPU變數,前面的[NN] 表示 CPU 的 ID, 後面的16進位數字表示該變數的地址.

2. OK, 我們隨便在某個 CPU 上構造 softlockup 事件.比如 CPU 31, 那麼它的變數地址是 ffff88403fded428, 我們讀一下它的值:

crash&> rd ffff88403fded428

ffff88403fded428: 00000000000ed290

Ok, 這個 ed290 的16進位數字是它的值,它是一個時間戳.

3.那麼,我們的 hacky 方式來了,把這個時間戳人為改到很早前(保證比當前時間戳早 20s 以上就可以),就能構造 softlockup 的事件了.比如

crash&> wr -k ffff88403fded428 00000000000e02e9

4.事實上,當我把上面的命令輸入後,馬上系統就 softlockup 了,由於我也設置 /proc/sys/kernel/softlockup_panic 為1, 所以系統就 panic 了.

搞定!出事別怪我.

-----

更新:

Q: 既然有 softlockup, 那也有 hardlockup 吧?

A: 有的。hardlockup ,顧名思義,就是系統真真正正鎖死了。它就是指:系統上的中斷給屏蔽超過一定時限了。由於調度,時鐘等重要內核事件都是要時間中斷來觸發的,現在中斷給屏蔽了,就導致這一切都無法觸發,不說調度,連各種重要的時鐘事件都無法處理了。系統基本啥也幹不了了,所以就呈現 hung 死狀態,什麼也做不了。相比較,softlockup 還只是調度器無法調度,但當前在 CPU 上跑的任務還是能一直跑的,就是沒人來搶佔它了而已。所以,它呈現的是另一種 hung 死狀態。某些進程在跑,但系統似乎沒反應了。

Q: 那麼系統如何監控 hardlockup 情況呢?

A: 由於系統自己完全 hung 死了,所以靠它自身是沒有辦法的。但是,中斷雖然屏蔽了,可是,每種平台上,都有無法屏蔽的中斷(NMI, Non-Maskable Interrupt)。所以,系統需要依賴一個外部硬體,系統會定時向它問好,如果系統 hardlockup 了,這個外部硬體一發現系統好久沒來請安,就給它發送一個 NMI, 系統的 NMI 中斷處理函數就會處理這種情況,一般這時,系統也無能為力,它的做法一般都是讓自己 panic, 生成 coredump, 讓用戶去收屍驗屍。

Q: 為什麼要屏蔽中斷?

A: 大部分時間,系統都是開著中斷跑的。但有一些重要的臨界區,中斷需要關掉一小小小會兒。比如在中斷處理函數中,需要關掉一小小會,以避免中斷嵌套。當然,這個關中斷的時間是內核要儘力保證很短的。所以,在實時內核中,要千方百計地減少延遲,其中一種方法就是盡量減少內核中關中斷的臨界區數量。


個人理解想辦法禁止調度就可以了。

比如spinlock之後死循環。。。

長時間陷入定時器。。。


寫一個線程或者進程,裡面的內容是禁止搶佔,mdelay.把這個線程綁定到你想要卡死的CPU上。

....

preempt_disable();

mdelay(1000 * 1000);

....

如果要hardlock up,順便把中斷給禁了


推薦閱讀:

如何正確開發 linux bsp?
linux mmap匿名映射的作用是什麼?
segfault at xxx的地址是物理地址還是線性地址,objdump出來的呢?
Linux的啟動過程中,滿足什麼條件可以不用initramfs而直接掛載/?

TAG:操作系統 | Linux系統管理 | Linux內核 |