一、調(diào)試前的準備
內(nèi)核級bug具有行為不可靠,定義不清晰或者說很難再現(xiàn)的諸多特定,為內(nèi)核級的bug跟蹤和調(diào)試帶來了很大的困難。
對于一些定義不清楚地bug,問題的關鍵就是找到bug的源頭,很多時候,當你精確地重現(xiàn)一個bug的時候,你就離成功不遠了。
二、內(nèi)核中的bug
從隱藏在源代碼中的錯誤到展現(xiàn)在目擊者面前的bug,其發(fā)作往往是一系列連鎖反應的事件才可能出發(fā)的。
雖然內(nèi)核調(diào)試有一定的困難,但是通過你的努力和理解,說不定你會喜歡上這樣的挑戰(zhàn)。
三、printk( )
內(nèi)核提供的格式化打印函數(shù)。
1、printk函數(shù)的健壯性
健壯性是printk最容易被接受的一個特質(zhì),幾乎在任何地方,任何時候內(nèi)核都可以調(diào)用它(中斷上下文、進程上下文、持有鎖時、多處理器處理時等)。
在系統(tǒng)啟動過程中,終端初始化之前,在某些地方是不能調(diào)用的。
2、記錄等級
printk函數(shù)可以指定一個記錄級別,內(nèi)核根據(jù)這個級別來判斷是否在終端上打印消息。
記錄級別定義在<linux/kernel.h>中。
調(diào)用方式:printk(KER_DEBUG “This is a debug notice!/n”); 內(nèi)核用這個指定的紀錄等級和當前終端的紀錄等級console_loglevel比較,來決定是不是向終端打印。
3、記錄緩沖區(qū)
內(nèi)核消息都被保存在一個LOG_BUF_LEN大小的環(huán)形隊列中。
關于LOG_BUF_LEN定義:
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
變量CONFIG_LOG_BUF_SHIFT在內(nèi)核編譯時由配置文件定義,對于i386平臺,其值定義如下(在linux26/arch/i386/defconfig中):
CONFIG_LOG_BUF_SHIFT=18
緩沖區(qū)操作:
①消息被讀出到用戶空間時,此消息就會從環(huán)形隊列中刪除。
②當消息緩沖區(qū)滿時,如果再有printk()調(diào)用時,新消息將覆蓋隊列中的老消息。
③在讀寫環(huán)形隊列時,同步問題很容易得到解決。
這個紀錄緩沖區(qū)之所以稱為環(huán)形,是因為它的讀寫都是按照環(huán)形隊列的方式進行操作的。
4、syslogd和klogd
在標準的Linux系統(tǒng)上,用戶空間的守護進程klogd從紀錄緩沖區(qū)中獲取內(nèi)核消息,再通過syslogd守護進程把這些消息保存在系統(tǒng)日志文件中。klogd進程既可以從/proc/kmsg文件中,也可以通過syslog()系統(tǒng)調(diào)用讀取這些消息。默認情況下,它選擇讀取/proc方式實現(xiàn)。klogd守護進程在消息緩沖區(qū)有新的消息之前,一直處于阻塞狀態(tài)。一旦有新的內(nèi)核消息,klogd被喚醒,讀出內(nèi)核消息并進行處理。默認情況下,處理例程就是把內(nèi)核消息傳給syslogd守護進程。
syslogd守護進程一般把接收到的消息寫入/var/log/messages文件中。不過,還是可以通過/etc/syslog.conf文件來進行配置,可以選擇其他的輸出文件。
四、OOPS
作為內(nèi)核的開發(fā)者,必定將會經(jīng)常處理OOPS。
1、ksymoops
在 Linux 中,調(diào)試系統(tǒng)崩潰的傳統(tǒng)方法是分析在發(fā)生崩潰時發(fā)送到系統(tǒng)控制臺的 Oops 消息。一旦您掌握了細節(jié),就可以將消息發(fā)送到 ksymoops 實用程序,它將試圖將代碼轉換為指令并將堆棧值映射到內(nèi)核符號。
ksymoops需要幾項內(nèi)容:Oops 消息輸出、來自正在運行的內(nèi)核的 System.map 文件,還有 /proc/ksyms、vmlinux 和 /proc/modules。
關于如何使用 ksymoops,內(nèi)核源代碼 /usr/src/linux/Documentation/oops-tracing.txt 中或 ksymoops 手冊頁上有完整的說明可以參考。Ksymoops 反匯編代碼部分,指出發(fā)生錯誤的指令,并顯示一個跟蹤部分表明代碼如何被調(diào)用。
#cat /proc/kallsyms
c0100240 T _stext
c0100240 t run_init_process
c0100240 T stext
c0100269 t init
五、內(nèi)核調(diào)試配置選項
在編譯內(nèi)核的時候,為了方便調(diào)試和測試代碼,內(nèi)核提供了許多配置選項。
調(diào)試原子操作
從內(nèi)核2.5開發(fā),為了檢查各類由原子操作引發(fā)的問題,內(nèi)核提供了極佳的工具。
內(nèi)核提供了一個原子操作計數(shù)器,它可以配置成,一旦在原子操作過程中,進城進入睡眠或者做了一些可能引起睡眠的操作,就打印警告信息并提供追蹤線索。
所以,包括在使用鎖的時候調(diào)用schedule(),正使用鎖的時候以阻塞方式請求分配內(nèi)存等,各種潛在的bug都能夠被探測到。
下面這些選項可以最大限度地利用該特性:
CONFIG_PREEMPT = y
CONFIG_DEBUG_KERNEL = y
CONFIG_KLLSYMS = y
CONFIG_SPINLOCK_SLEEP = y
六、引發(fā)bug并打印信息
1、一些內(nèi)核調(diào)用可以用來方便標記bug,提供斷言并輸出信息。最常用的兩個是BUG()和BUG_ON()。
當調(diào)用這兩個宏的時候,它們會引發(fā)OOPS,導致棧的回溯和錯誤消息的打印。
2、dump_stack()
有些時候,只需要在終端上打印一下棧的回溯信息來幫助你調(diào)試。這時可以使用dump_stack()。這個函數(shù)只在終端上打印寄存器上下文和函數(shù)的跟蹤線索。
if (!debug_check) {
printk(KERN_DEBUG “provide some information…/n”);
dump_stack();
}
下一個教程:返回列表