Linux panic 思路

轉載自:https://blog.csdn.net/ylyuanlu/article/details/9115159

1. Linux Kernel Panic的產生的原因

     panic是英文中是驚慌的意思,Linux Kernel panic正如其名,linux kernel不知道如何走了,它會儘可能把它此時能獲取的全部信息都打印出來。

有兩種主要類型kernel panic,後面會對這兩類panic做詳細說明:

1.hard panic(也就是Aieee信息輸出)
2.soft panic (也就是Oops信息輸出)

2. 常見Linux Kernel Panic報錯內容:

(1) Kernel panic-not syncing fatal exception in interrupt
(2) kernel panic – not syncing: Attempted to kill the idle task!
(3) kernel panic – not syncing: killing interrupt handler!
(4) Kernel Panic – not syncing:Attempted to kill init !

3. 什麼會導致Linux Kernel Panic?
     只有加載到內核空間的驅動模塊才能直接導致kernel panic,你可以在系統正常的情況下,使用lsmod查看當前系統加載了哪些模塊。
除此之外,內建在內核裏的組件(比如memory map等)也能導致panic。

     因爲hard panic和soft panic本質上不同,因此我們分別討論。

4. hard panic
一般出現下面的情況,就認爲是發生了kernel panic:

機器徹底被鎖定,不能使用
數字鍵(Num Lock),大寫鎖定鍵(Caps Lock),滾動鎖定鍵(Scroll Lock)不停閃爍。
如果在終端下,應該可以看到內核dump出來的信息(包括一段”Aieee”信息或者”Oops”信息)
和Windows藍屏相似
4.1 原因

      對於hard panic而言,最大的可能性是驅動模塊的中斷處理(interrupt handler)導致的,一般是因爲驅動模塊在中斷處理程序中訪問一個空指針(null pointre)。一旦發生這種情況,驅動模塊就無法處理新的中斷請求,最終導致系統崩潰。

     本人就曾遇到過這樣一個例子:在多核系統中,包括AP應用處理器、mcu微控制器和modem處理器等系統中,mcu控制器用於系統的低功耗控制,mcu微控制器由於某種原因超時向AP應用處理器發送一個超時中斷,AP接受中斷後調用中斷處理函數讀取mcu的狀態寄存器,發現是mcu的超時中斷,就在中斷處理程序中主動引用一個空指針,迫使AP處理器打印堆棧信息然後重啓linux系統。這就是一個典型的hard panic,這裏不對mcu超時原因做深入的分析,只是用來說明hard panic產生的機理。

3.2 信息收集

      根據panic的狀態不同,內核將記錄所有在系統鎖定之前的信息。因爲kenrel panic是一種很嚴重的錯誤,不能確定系統能記錄多少信息,下面是一些需要收集的關鍵信息,他們非常重要,因此儘可能收集全,當然如果系統啓動的時候就kernel panic,那就無法只知道能收集到多少有用的信息了。

/var/log/messages: 幸運的時候,整個kernel panic棧跟蹤信息都能記錄在這裏,當然對於嵌入式linux系統,kernel panic的內核打印信息被放到/data/dontpanic目錄下,包括兩個文件:apanic_console存放的是內核控制檯的log,apanic_threads存放的是linux kernel發生panic時的所有內核線程的堆棧信息。
應用程序/庫 日誌: 可能可以從這些日誌信息裏能看到發生panic之前發生了什麼。
其他發生panic之前的信息,或者知道如何重現panic那一刻的狀態
終端屏幕dump信息,一般OS被鎖定後,複製,粘貼肯定是沒戲了,因此這類信息,你可以需要藉助數碼相機或者原始的紙筆工具了。
 如果kernel dump信息既沒有在/var/log/message裏,也沒有在屏幕上,那麼嘗試下面的方法來獲取(當然是在還沒有死機的情況下):

如果在圖形界面,切換到終端界面,dump信息是不會出現在圖形界面的,甚至都不會在圖形模式下的虛擬終端裏。
確保屏幕不黑屏,可以使用下面的幾個方法:
setterm -blank 0
setterm -powerdown 0
setvesablank off
從終端,拷貝屏幕信息(方法見上)
     實際上,當內核發生panic時,linux系統會默認立即重啓系統,當然這只是默認情況,除非你修改了產生panic時重啓定時時間,這個值默認情況下是0,即立刻重啓系統。所以當panic時沒有把kernel信息導入文件的話,那麼可能你很難再找到panic產生的地方。
3.3 完整棧跟蹤信息的排查方法

      棧跟蹤信息(stack trace)是排查kernel panic最重要的信息,該信息如果在/var/log/messages日誌裏當然最好,因爲可以看到全部的信息,如果僅僅只是在屏幕上,那麼最上面的信息可能因爲滾屏消失了,只剩下棧跟蹤信息的一部分。如果你有一個完整棧跟蹤信息的話,那麼就可能根據這些充分的信息來定位panic的根本原因。要確認是否有一個足夠的棧跟蹤信息,你只要查找包含”EIP”的一行,它顯示了是什麼函數和模塊調用時導致panic。

使用內核調試工具(kenrel debugger ,aka KDB)

如果跟蹤信息只有一部分且不足以用來定位問題的根本原因時,kernel debugger(KDB)就需要請出來了。
KDB編譯到內核裏,panic發生時,他將內核引導到一個shell環境而不是鎖定。這樣,我們就可以收集一些與panic相關的信息了,這對我們定位問題的根本原因有很大的幫助。

使用KDB需要注意,內核必須是基本核心版本,比如是2.4.18,而不是2.4.18-5這樣子的,因爲KDB僅對基本核心有效。

4. soft panic
4.1 症狀:

沒有hard panic嚴重
通常導致段錯誤(segmentation fault)
可以看到一個oops信息,/var/log/messages裏可以搜索到’Oops’
機器稍微還能用(但是收集信息後,應該重啓系統)
4.2 原因

      凡是非中斷處理引發的模塊崩潰都將導致soft panic。在這種情況下,驅動本身會崩潰,但是還不至於讓系統出現致命性失敗,因爲它沒有鎖定中斷處理例程。導致hard panic的原因同樣對soft panic也有用(比如在運行時訪問一個空指針)

4.3 信息收集

       當soft panic發生時,內核將產生一個包含內核符號(kernel symbols)信息的dump數據,這個將記錄在/var/log/messages裏。爲了開始排查故障,可以使用ksymoops工具來把內核符號信息轉成有意義的數據。

爲了生成ksymoops文件,需要:

從/var/log/messages裏找到的堆棧跟蹤文本信息保存爲一個新文件。確保刪除了時間戳(timestamp),否則ksymoops會失敗。
運行ksymoops程序(如果沒有,請安裝)
詳細的ksymoops執行用法,可以參考ksymoops(8)手冊。
5. Kernel panic實例:
      今天就遇到 一個客戶機器內核報錯:“Kernel panic-not syncing fatal exception”,重啓後正常,幾個小時後出現同樣報錯,系統down了,有時重啓後可恢復有時重啓後仍然報同樣的錯誤。

      什麼是fatal exception?

     “致命異常(fatal exception)表示一種例外情況,這種情況要求導致其發生的程序關閉。通常,異常(exception)可能是任何意想不到的情況(它不僅僅包括程序錯誤)。致命異常簡單地說就是異常不能被妥善處理以至於程序不能繼續運行。

     軟件應用程序通過幾個不同的代碼層與操作系統及其他應用程序相聯繫。當異常(exception)在某個代碼層發生時,爲了查找所有異常處理的代碼,各個代碼層都會將這個異常發送給下一層,這樣就能夠處理這種異常。如果在所有層都沒有這種異常處理的代碼,致命異常(fatal exception)錯誤信息就會由操作系統顯示出來。這個信息可能還包含一些關於該致命異常錯誤發生位置的祕密信息(比如在程序存儲範圍中的十六進制的位置)。這些額外的信息對用戶而言沒有什麼價值,但是可以幫助技術支持人員或開發人員調試程序。

     當致命異常(fatal exception)發生時,操作系統沒有其他的求助方式只能關閉應用程序,並且在有些情況下是關閉操作系統本身。當使用一種特殊的應用程序時,如果反覆出現致命異常錯誤的話,應將這個問題報告給軟件供應商。 ” 而且此時鍵盤無任何反應,必然使用reset鍵硬重啓。

     panic.c源文件有個方法,當panic掛起後,指定超時時間,可以重新啓動機器,這就是前面說的panic超時重啓。如果你的機器事先配置好了魔法鍵的使用,就可以在超時之前通過魔法鍵使系統在重啓前儘可能多的爲你多做些事情,當然這些事情不是用來使系統恢復正常,而是儘量避免損失或導出一些有用信息來幫助後面的定位。

方法:

#vi /etc/sysctl.conf  添加

kernel.panic = 20 #panic error中自動重啓,等待timeout爲20秒
kernel.sysrq=1 #激活Magic SysRq  否則,鍵盤鼠標沒有響應

按住 [ALT]+[SysRq]+[COMMAND], 這裏SysRq是Print SCR鍵,而COMMAND按以下來解釋!

b – 立即重啓
e – 發送SIGTERM給init之外的系統進程
o – 關機
s – sync同步所有的文件系統
u – 試圖重新掛載文件系統

配置一下以防萬一。

     很多網友安裝linux出現“Kernel panic-not syncing fatal exception in interrupt”是由於網卡驅動原因。解決方法:將選項“Onboard Lan”的選項“Disabled”,重啓從光驅啓動即可。等安裝完系統之後,再進入BIOS將“Onboard Lan”的選項給“enable”,下載相應的網卡驅動安裝。

    如出現以下報錯:

init() r8168 … 

          … …

         … :Kernel panic: Fatal exception

r8168是網卡型號。

在BIOS中禁用網卡,從光驅啓動安裝系統。再從網上下載網卡驅動安裝。

#tar  vjxf  r8168-8.014.00.tar.bz2

# make  clean  modules       (as root or with sudo)

      # make  install

      # depmod  -a

      # modprobe  r8168

安裝好系統後reboot進入BIOS把網卡打開。

    另有網友在Kernel panic出錯信息中看到“alc880”,這是個聲卡類型。嘗試着將聲卡關閉,重啓系統,搞定。

    安裝linux系統遇到安裝完成之後,無法啓動系統出現Kernel panic-not syncing fatal exception。很多情況是由於板載聲卡、網卡、或是cpu 超線程功能(Hyper-Threading )引起的。這類問題的解決辦法就是先查看錯誤代碼中的信息,找到錯誤所指向的硬件,將其禁用。系統啓動後,安裝好相應的驅動,再啓用該硬件即可。

    另外出現“Kernel Panic — not syncing: attempted to kill init”和“Kernel Panic — not syncing: attempted to kill idle task”有時把內存互相換下位置或重新插拔下可以解決問題。

6. 一個kernel panic的解決之法

    相信使用linux kernel開發過驅動的兄弟都知道,kernel panic對系統帶來的危害要比應用程序panic大的多,甚至可以用災難來形容。對於應用程序的panic最多導致linux系統殺掉該用戶進程,但對於kernel panic就沒辦法了,因爲kernel是整個系統的管理者,自己出現問題了(當然是不可恢復的異常)就只能等待重啓了。

    kernel panic的最大問題就是難於定位,對於一個開發者來說,有些kernel panic那簡直就像是一場噩夢,上面主要說明了如何抓取kernel panic的方法和一些panic實例,當然,抓取panic的打印信息是解決panic的第一步也是關鍵一步,下面就根據自己曾碰到過的一個kernel panic做爲實例來說明從出現panic到解決panic的一般方法。

6.1 抓取kernel panic信息

     沒錯,正如前面說的,這是第一步也是非常關鍵的一步,如果要解決一個kernel panic當然必須首先要知道它產生的地方,也就是說產生panic的內核函數調用棧,當前的內核調用棧記錄了產生kernel panic時的函數調用關係鏈,這裏我不在貼出相關的打印實例,這樣的kernel panic網上也到處都是,而且還有很多的文章來說明如何確定是哪個源文件的哪一行導致的panic,因此感興趣的同學可以搜索一些這樣的文章看看,這裏指說明一下解決kernel panic的一般步驟和注意事項。

     對於抓取kernel log的方法前面有介紹,這裏不贅述,但想強調兩點:

     (1) 不管是什麼樣的panic,首先要抓取足夠的內核打印信息,當然必要的情況下還需要蒐集產生kernel panic時的應用程序的打印信息,對於android系統來說就是logcat信息,在android嵌入式軟件平臺上其實有更好更全面的log蒐集方法,那就是bugreport,它將產生此刻系統全方位的信息,對,沒錯,就是全方位的信息,包括內核、應用、內存、進程和處理器等所有相關信息,是一個非常好的調試工具,至於bugreport的工作原理感興趣的同學自己查找下資料。

       注意:bugreport的使用需注意兩點:第一,它只能在系統正常運行的情況下使用,第二,正因爲第一點,你需要在系統產生kernel panic重啓系統後的第一時間使用bugreport導出所有信息,因爲這所有信息中包含了上次系統重啓的原因的相關log信息。

     (2) 既然是抓取panic log信息,必然少不了復現panic這個過程,有的panic的產生時概率性隨機的,就是說你不知道什麼時候就可能會產生panic,因此請珍惜每一次復現panic的機會,起碼要在復現panic之前準備好你要抓取的是那些信息,這些信息能否幫助你進一步定位panic,否則,不要在出現panic時手忙腳亂,不知道自己要什麼,最好每次復現panic前計劃好這次你要那些信息(可能每次抓取信息的重點不一樣)。

      注意:在工作中經常碰到這樣一個現象:測試部門的同學好不容易發現一個問題,請開發同學定位,開發同學基本上沒怎麼分析問題就嚷着信息抓的不夠沒法定位,結果讓測試同學半天甚至一天來複現這個問題,等復現了問題開發同學還沒搞清楚自己到底要什麼信息來定位,有的問題復現時的環境只能保持幾分鐘甚至幾十秒鐘,這勢必會浪費了測試同學的勞動成果。

6.2 分析kernel panic

     蒐集了足夠的panic信息,下面就是分析panic的時候了,對於一個panic問題,你要知道三點:

   (1) 首先要對彙編語言有一定的瞭解,定位panic產生的C代碼位置

      其實就是根據當前內核線程的內核調用棧查找產生panic調用鏈,在panic log的前面幾行已經顯示了kernel panic的代碼位置,但這個位置是相對於產生panic函數的偏移,你並不知道它到底是哪一行,這個時候你需要objdump反彙編器來對那個產生panic的鏡像文件反彙編,然後根據panic信息的指示找到對應的彙編代碼,對照C代碼根據彙編上下文確定C代碼行,其實,kernel panic的產生一般都是非法地址的引用,尤其是NULL指針的引用,這也比較容易定位出panic的C代碼行。

   (2) 分析導致panic的C代碼行上下文,確定panic引入點

      第一步應該會比較容易找到導致panic 的C代碼行,根據產生panic的代碼進一步找到panic的引入點,這一步可以搭配printk來定位(如果是大概率panic就更容易定位了),這一步相對第一步花費多一點的時間,如果是應用代碼分析到這裏已經差不多結束了,確定了panic引入點就可以修改代碼進行迴歸測試了,但對於kernel來說要複雜的多。

      正如之前曾碰到的panic,復現雖然不容易但是基本上在固定時間點左右就可以復現,我是用的腳本循環加載卸載wifi模塊,每次都是大約500次左右產生panic,要知道必現的panic就容易解決的多了,但當時因爲這500次的循環就要花費2個小時左右,而且環境還經常出現問題,導致我花費很長時間才定位出問題所在:每次的加載和卸載wifi模塊都導致devices kset節點引用計數多減一,當devices kset的引用計數變爲0的時候被系統回收,linux系統隨後可能會出現N種panic現象,之後發現是因爲wifi模塊每次加載下載時對應的設備節點的引用計數增減失衡導致devices kset被多減一,然後發現是linux 內核核心代碼的問題。

    (3) 最好不要懷疑linux的核心代碼,也不要試圖去修改

      正因爲這一點,讓我遲遲不敢確定是不是真的核心代碼問題,linux的核心代碼那可是數以萬計的大牛經過千錘百煉的代碼,豈容你輕易修改,經過進一步的分析這個panic是因爲我們用的wifi卡是非標準媒體卡,走的是非標準流程,在這個流程中對wifi設備初始化時少了一次wifi設備節點的引用,但在卸載模塊時同標準卡一樣被解引用了。

    (4)不要堅定的以爲圍繞着panic信息就能解決panic問題

      還是上面的panic,實際上,上面提到的panic問題其實應該是很多的panic,這也是在後期復現panic時發現的,在加載卸載500次左右時必現panic,但卻不是同一個panic,如果按照正常思路:既然是panic,就應該從panic信息下手,順藤摸瓜一直追下去。如果是這樣,這個問題恐怕永遠也解決不了,因爲你在復現一個panic時總是會有其他的panic出現,這回讓你無所適從的。

       通過對這些panic的log的分析,發現他們都有一個共性,在產生panic之前都有一段WARNING打印,也正是對這段打印的分析找出了問題的根源,對於這段WARNING的內容和分析過程不在這裏說明,只爲表達下面的觀點:

       事實證明,在碰到panic問題定位時,如果想當一段時間內定位不出來,又沒有什麼更好的思路時,你應該回頭看看在panic之前kernel是否產生了哪些不太正常的log,這也許就是導致kernel panic的前兆或推手。

     (5) 儘可能多的把握linux kernel的行爲,對一些難啃的panic大膽猜測

       這裏的大膽猜測是建立在想當了解linux kernel行爲上的有理性的推理,儘管有些猜測並不是完全正確的,但在你證明它是正確的過程中或許會有意外收穫。對於wifi模塊加載卸載的panic問題來說,我曾有過兩次錯誤的猜測:

       第一次,因爲長時間的加載卸載都會出現panic,而且開始發現的panic是在kmem_cache_alloc函數中,因此猜測是內存泄露導致的內存耗盡,因此在後面的復現過程中我寫了個腳本循環打印內存的使用情況,發現內存的佔用一直穩定在一個正常的範圍,證明了我的第一個猜測是錯誤的。

      第二次,在看到devices 的內核對象kobject的名字在panic之前出現亂碼的情況下(printk打印了名字),我曾大膽地做過猜測:linux kernel發生了踩內存現象,導致devices kset對象被破壞。隨後做了各方面的努力來證明我的想法,結果發現幾乎都是在第493次加載wifi模塊時出現的panic問題,這讓我很迷惑,如果是踩內存怎麼可能固定在493次發生,雖然不能完全證明這個猜想是錯誤的,但這足以說明我的方向有問題。

     如此反反覆覆,花費了我兩個星期的時間才搞定這個panic,因此,kernel panic雖然難啃,但只要你願意去嘗試願意去努力,就算最後拿不下這個panic,你也會學到很多很多的東西,包括linux kernel行爲,這些會對你以後的學習產生很大的影響,在碰到這類問題一定是信心滿滿的。

7. 小結

     一直想總結一點kernel panic的解決之法,在網上也搜索了很多資料,基本上都一樣,本文前面也引用了這些文章中的一篇,曾經做過的總結過的東西能記錄下來給別人看和給以後自己複習都是很有意義的事情,以前kernel panic的問題總讓我不敢靠的太近,現在我還是可以比較自信的面對他們,這裏也只是給同學們一些解決panic的建議,個人覺得分析一個具體的實例的意義也不是太大,所以也沒有對一個具體的實例做詳細分析,希望可以找到更多的相關文章來拜讀,夜已深沉,還有什麼人...

參考:http://blog.csdn.net/qingmudaxin/article/details/8954961
 

發佈了46 篇原創文章 · 獲贊 10 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章