內核調試linux
內核中的bug多種多樣,表象也變化無窮,有一些獨特的問題須要考慮。git
1、經過打印來調試(printk())編輯器
1.健壯性:函數
printk是一個彈性極佳的函數,尤爲是隨時都能被調用的特性(除了終端沒有初始化以前--可用early_printk()解決)工具
2.日誌等級oop
printk與printf最主要的區別是printk能夠指定日誌級別,內核根據級別判斷是否在終端上打印消息(比某個特定值低的消息顯示在終端上)ui
可供使用記錄等級:spa
KERN_EMERG | 一個緊急狀況 |
KERN_ALERT | 一個須要當即被注意到的錯誤 |
KERN_CRIT | 一個臨界狀況 |
KERN_ERR | 一個錯誤 |
KERN_WARNING | 一個警告 |
KERN_NOTICE | 一個普通的,不過可能須要注意的狀況 |
KERN_INFO | 一條非正式的消息 |
KERN_DEBUG | 一條調試信息——通常是冗餘信息 |
沒指定記錄等級時,會選用默認的DEFAULT_MESSAGE_LOGLEVEL,如今是KERN_WARNING調試
最重要的記錄等級KERN_EMERG定爲「<0>」日誌
3.記錄緩衝區
內核消息被保存在一個LOG_BUF_LEN大小的環形隊列中,大小可經過設置CONFIG_LOG_BUF_SHIFT調整。(但處理器默認16kb)
環形隊列好處:同時讀寫時,同步問題容易解決;
記錄維護也更容易。
缺點:可能會丟失消息。
4.syslogd和klogd
用戶空間的守護進程klogd從記錄緩衝區獲取內核消息,syslogd守護進程保存在系統日誌文件中(默認是/var/log/messages,也可經過/etc/syslog.conf設置)。
讀取方法:1)從/proc/kmsg文件中
2)經過syslog()系統調用;
但兩種方法klogd都會阻塞。
啓動klogd時可經過指定-c標誌改變終端記錄等級。
2、oops
內核很難自行修復,也不能將本身殺死,只能發佈oops
1.ksymoops
回溯線索中的地址須要轉化成有意義的符號名稱,須要調用ksymoops,以及System.map
使用:
ksymoops saved_oops.txt
2.kallsyms
開發版的2.5引入了kallsyms特性。解碼再也不須要System.map或ksymoops了。
3、內核調試配置選項
位於內核配置編輯器的內核開發菜單項中,都依賴與CONFIG_DEBUG_KERNEL
4、引起bug並打印信息
經常使用BUG()和BUG_ON()標記bug,提供斷言並輸出信息。
if (bad_thing) BUG();
或
BUG_ON(bad_thing);
還可用panic()引起更嚴重的錯誤,會掛起整個系統,只應該在最糟糕的狀況使用。
dump_stack()只在終端上打印寄存器上下文和函數的跟蹤線索。
5、系統請求鍵
1.可經過定義CONFIG_MAGIC_SYSRQ配置選項啓用。被啓用時,不管內核處於什麼狀態,都能經過特殊組合鍵與內核通訊。
啓用命令:
echo 1 > /proc/sys/kernel/sysrq
2.SysRq-h:獲取一份可用的選項列表
SysRq-s:將「髒」緩衝區與硬盤交換分區同步
SysRq-u:卸載全部文件系統
SysRq-b:重啓設備(更多見P303)
3.系統請求鍵是調試和挽救垂危系統所必須的工具
6、調試器
1.gdb
gdb vmlinux /proc/kcore
vmlinux爲未壓縮的內核映像,經過/proc/kcore訪問內核駐留的高端內存。
但不要把編譯帶調試信息的內核當作習慣(編譯出的內核大不少)
侷限性:不能修改內核數據;不能但不執行內核代碼;不能加斷點
2.kgdb
是一個補丁,可以讓咱們在遠端主機上經過串口利用gdb對內核調試
7、探測系統
1.UID做爲選擇條件
開發進程相關部分時,保證提供替代物的同時不破壞原有代碼的可執行性;可用用戶id做爲選擇條件實現:
如:
if (current->uid != 7777){ /*old method...*/ } else { /*new method...*/ }
2.使用條件變量(代碼與進程無關或有針對全部狀況都能使用的機制來控制某個特性)
3.使用統計量(用於掌握某個特定事件發生規律時)
4.重複頻率限制
8、使用Git進行二分搜索
$git bisect start /*進行二分搜索*/ $git bisect bad <revision>/*提供出現問題的最先內核版本*/ $git bisect good v2.6.28/*提供可正常運行的版本*/
也可指定git僅在與錯誤相關的目錄列表中去二分搜索提交的補丁。