《Linux內核設計與實現》Chapter 18 讀書筆記


《Linux內核設計與實現》Chapter 18 讀書筆記

一、准備開始

  • 一個bug
  • 一個藏匿bug的內核版本
    • 知道這個bug最早出現在哪個內核版本中。
  • 相關內核代碼的知識和運氣

想要成功進行調試:

  • 讓這些錯誤重現
  • 抽象出問題
  • 從代碼中搜索

二、內核中的bug

1.內核bug的表象:

錯誤代碼
同步時發生的錯誤,例如共享變量鎖定不當
錯誤的管理硬件
降低所有程序的運行性能
毀壞數據
使得系統處於死鎖狀態
……
  • 引用空指針會產生一個oops;垃圾數據會導致系統崩潰。

三、通過打印來調試

內核提供打印函數printk(),他是內核的格式化打印函數,它還有自己的一些特殊的功能:

1.健壯性

健壯性即在任何時候,任何地方都能調用它。

  • 在中斷上下文和進程上下文中被調用
  • 在任何持有鎖時被調用
  • 在多處理器上同時被調用,並且不必使用鎖。

printk()有用之處在於它隨時都能被調用。

注意:該函數在終端沒有初始化之前,某些地方不能用它。這種情況,需要用early_printk()代替,它在啟動初期就可以在終端上打印,兩者功能完全相同,但early_printk()缺少可移植性。

2.日志等級

printk()和printf()在使用上最主要的區別就是前者可以指定一個日志級別,內核根據這個級別來判斷是否在終端上打印消息。內核把級別比某個特定值低的所有消息顯示在終端上。

下表列舉了所有可用的記錄等級。

enter description here
如果沒有特別特別指定記錄等級,函數會選用默認的DEFAULT_MESSAGE_LOGLEVEL,當前默認等級是KERN_WARNING,但它存在着變化的可能性。、,最好還是給自己的消息指定一個記錄等級。
內核將最重要的等級KERN_EMERG定為"<0>",將最無關緊要的等級KERN_DEBUG定為"<7>",從0-7,對應表中從上到下

3.記錄緩沖區

內核消息是被保存在一個LOG_BUF_LEN大小的環形隊列中,該緩沖區的大小是可以在編譯時設置CONFIG_LOG_BUF_SHIFT進行調整,但是在單處理器的系統上默認值是16kb。就是內核在同一時間只能保存16kb的內核消息,否則新消息就會覆蓋老消息。該緩沖區之所以稱為環形,因為讀寫都是按照環形隊列方式操作的。

 

(1)環形隊列的優點:

  • 讀寫同步問題容易解決記錄
  • 記錄的維護更加方便

(2)缺點:

  • 可能會丟失消息

4.syslogd和klogd

用戶空間的守護進程klogd從記錄緩沖區中獲取內核消息,再通過syslogd守護進程將他們保存在系統日志文件中。

(1)klogd

  • 既可以從/proc/kmsg文件中,也可以通過syslog()系統調用讀取這些消息。默認選擇讀取/proc方式實現。
  • 兩種方法klogd都會阻塞,直到有新的內核消息可供讀出,喚醒之后將消息傳給syslogd。
  • 啟動時,可以通過-c標志來改變終端的記錄等級。

(2)syslogd

  • 將它接收到的所有消息添加到一個文件中,該文件默認是/var/log/messages。
  • 也可以通過/etc/syslog.conf配置文件重新指定。

5.從printf()到printk()的轉換

四、oops

oops是內核告知用戶有不幸發生的最常用的方式。

1.發布oops的過程:

向終端上輸出錯誤消息
輸出寄存器中保存的信息
輸出可供跟蹤的回溯線索

通常發送完oops之后,內核會處於一種不穩定狀態。

2.關於oops發生的時機:

  1. 發生在中斷上下文:內核根本無法繼續,會陷入混亂,結果導致系統死機
  2. 發生在idle進程(pid為0)或init進程(pid為1),系統也會陷入混亂
  3. 發生在其他進程運行時,內核會殺死該進程並嘗試着繼續執行

3.oops發生的可能原因:

  1. 內存訪問越界
  2. 非法的指令
  3. ……

4.ksymoops

將回溯線索中的地址轉化成符號名稱,調用ksymoops命令並提供System.map。

調用方法:

  • ksymoops saved_oops.txt

5.kallsyms

現在的版本中不需要使用kysmoops這個工具,因為可能會發生很多問題,新版本中引入了kallsyms特性:

  • CONFIG_KALLSYMS 定義配置選項啟用。
  • CONFIG_KALLSYMS_ALL 存放函數名稱;存放所有符號名稱。
  • CONFIG_KALLSYMS_EXTRA_PASS 引起內核構建中再次忽略內核的目標代碼。

五、內核調試配置選項

1.配置選項

為了方便調試和測試內核代碼,內核提供了許多配置選項。它們都在內核配置編輯器的內核開發菜單項中,都依賴於CONFIG_DEBUG_KERNEL。

  • slab layer debugging slab層調試選項
  • high-memory debugging 高端內存調試選項
  • I/O mapping debugging I/O映射調試選項
  • spin-lock debugging 自旋鎖調試選項
  • stack-overflow debugging 棧溢出檢查選項
  • sleep-inside-spinlock checking 自旋鎖內睡眠選項

2.原子操作

指那些能夠不分隔執行的東西;在執行時不能中斷否則就是完不成的代碼。

六、引發bug並打印信息

1.最常用的內核調用:BUG()和BUG_ON()

被調用時會引發oops,導致棧的回溯和錯誤信息的打印。

大部分體系結構把它們定義成某種非法操作,可以把這些調用當做斷言使用,想要斷言某種情況不該發生:

if (bad_thing)
BUG();
或以更好的形狀:
BUG_ON(bad_thing);

多數開發者認為BUG_ON()比BUG()更清晰、更可讀。

2.BUILD_BUG_ON()

與BUG_ON()作用相同,僅在編譯時調用。

3.panic()

可以用其引發更嚴重的錯誤,它不但會打印錯誤信息,還會掛起整個系統,所以,只應該在最糟糕的情況下使用它。

4.dump_stack()

只在終端上打印寄存器上下文和函數的跟蹤線索。

七、神奇的系統請求鍵

神奇的系統請求鍵(Magic SysRq key)這個功能可以通過定義CONFIG_MAGIC_SYSRQ配置選項來啟用。SysRq(系統請求)鍵在大多數鍵盤上都是標准鍵。該功能被啟用時,無論內核出於什么狀態,都可以通過特殊的組合鍵和內核進行通信。

1.啟動命令

echo 1 > /proc/sys/kernel/sysrq

enter description here

八、內核調試器的傳奇

1.gdb

(1)針對內核啟動調試器的方法和對進程的方法大致相同

gdb vmlinux /proc/kcore

/*vmlinux文件是未壓縮的內核映像;

/proc/kcore是一個參數選項,作為core文件來用,只有超級用戶才能讀取此文件的數據。
*/

(2)打印一個變量的值

p global_variable

(3)反匯編一個函數

disassemble function

(4)局限性

  • 不能修改內核數據;
  • 不能單步執行內核代碼;
  • 不能加斷點

2.kgdb

是一個補丁,可在遠端主機上通過串口利用gdb的所有功能對內核進行調試。

九、探測系統

1.使用uid作為選擇條件

一般,只要保留原有的算法而把新算法加入到其他位置上,基本就能保證安全。可以把用戶id(UID)作為選擇條件來實現這種功能,通過某種選擇條件,安排到底執行哪種算法。

if (current-> uid !=7777) {
/* 老算法…… */
}
else {
/* 新算法…… */
}

除了uid=7777的用戶以外,其他所有的用戶都是用的老算法,所以這個7777用戶可以專門用來測試新算法。

2.使用條件變量

如果代碼與進程無關,或者希望有一個針對所有情況都能使用的機制來控制某個特性,可以使用條件變量。

(1)方法

創建一個全局變量作為一個條件選擇開關:

  • 如果該變量為0,就使用某一個分支上的代碼;
  • 否則,選擇另外一個分支。

3.使用統計量

需要掌握某個特定事件的發生規律的時候,通過創建統計量,並提供某種機制訪問其統計結果。

注意:這種實現不是SMP安全的

4.重復頻率限制

當系統的調試信息過多的時候,可以采取:

(1)重復頻率限制

(2)發生次數限制

十、用二分查找法找出引發罪惡的變更

十一、使用Git進行二分搜索

$ git bisect start  ;進行二分搜索
$ git bisect bad
<revision> ;引發提供一個出現問題的最高內核版本
$ git bisect bad ;若當前內核版本就是bug的元凶,那不必提供內核版本
$ git bisect good v2.
6.28 ;最新可正常運行的內核版本

$ git bisect good ;這個版本正常
$ git bisect bad ;這個版本有異常

十二、當所有努力都失敗時:社區

  • Linux內核郵件列表(LKML)

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com