如何製造一個內核態的軟鎖死?
事情是這樣的,最近在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: 00000000000ed290Ok, 這個 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而直接掛載/?