Linux 的核心調試

來源:互聯網
上載者:User
※  調試工作艱苦,是核心級開發區別於使用者級開發的一個顯著特點。※  駕馭核心調試的能力,很大程度上取決於經驗和對整個作業系統的把握。  一、調試前的準備   核心級bug具有 行為不可靠定義不清晰或者說很難再現的諸多特定,為核心級的bug跟蹤和調試帶來了很大的困難。   ※ 對於一些定義不清楚地bug,問題的關鍵就是找到 bug的源頭,很多時候,當你精確地重現一個bug的時候,你就離成功不遠了。  二、核心中的bug   從隱藏在原始碼中的錯誤到展現在目擊者面前的bug,其發作往往是一系列連鎖反應的事件才可能出發的。   雖然核心調試有一定的困難,但是通過你的努力和理解,說不定你會喜歡上這樣的 挑戰。  三、printk( ) 核心提供的格式化列印函數。 1、printk函數的健壯性    健壯性是printk最容易被接受的一個特質,幾乎在任何地方,任何時候核心都可以調用它(中斷上下文、進程上下文、持有鎖時、多處理器處理時等)。   ※ 在系統啟動過程中, 終端初始化之前,在某些地方是不能調用的。  2、記錄等級    printk函數可以指定一個記錄層級,核心根據這個層級來判斷是否在終端上列印訊息。    記錄層級定義在<linux/kernel.h>中:    

#define    KERN_EMERG    "<0>"   /* system is unusable */
#define    KERN_ALERT    "<1>"    /* action must be taken immediately    */
#define    KERN_CRIT        "<2>"    /* critical conditions */
#define    KERN_ERR        "<3>"    /* error conditions    */
#define    KERN_WARNING    "<4>"    /* warning conditions */
#define    KERN_NOTICE   "<5>"    /* normal but significant condition    */
#define    KERN_INFO        "<6>"    /* informational */
#define    KERN_DEBUG    "<7>"   /* debug-level messages    */  調用方式:printk(KER_DEBUG “This is a debug notice!/n”); 核心用這個指定的紀錄等級和當前終端的紀錄等級console_loglevel比較,來決定是不是向終端列印。  關於< linux/kernel.h >的console_loglevel 定義: #define console_loglevel (console_printk[0]) <printk.c>定義: int console_printk[4] = {           DEFAULT_CONSOLE_LOGLEVEL,  /* console_loglevel */           DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */           MINIMUM_CONSOLE_LOGLEVEL,  /* minimum_console_loglevel */           DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */ };  3、記錄緩衝區   核心訊息都被儲存在一個LOG_BUF_LEN大小的環形隊列中。   關於LOG_BUF_LEN定義:   #define __LOG_BUF_LEN     (1 << CONFIG_LOG_BUF_SHIFT)   ※ 變數CONFIG_LOG_BUF_SHIFT在核心編譯時間由設定檔定義,對於i386平台,其值定義如下(在linux26/arch/i386/defconfig中):   CONFIG_LOG_BUF_SHIFT=18     記錄緩衝區操作:   ①、訊息被讀出到使用者空間時,此訊息就會從環形隊列中刪除。   ②、當訊息緩衝區滿時,如果再有printk()調用時,新訊息將覆蓋隊列中的老訊息。   ③、在讀寫環形隊列時,同步問題很容易得到解決。   ※ 這個紀錄緩衝區之所以稱為環形,是因為它的讀寫都是按照環形隊列的方式進行操作的。  4、syslogd和klogd   在標準的Linux系統上,使用者空間的守護進程klogd從紀錄緩衝區中擷取核心訊息,再通過syslogd守護進程把這些訊息儲存在系統記錄檔中。klogd進程既可以從/proc/kmsg檔案中,也可以通過syslog()系統調用讀取這些訊息。預設情況下,它選擇讀取/proc方式實現。klogd守護進程在訊息緩衝區有新的訊息之前,一直處於阻塞狀態。一旦有新的核心訊息,klogd被喚醒,讀出核心訊息並進行處理。預設情況下,處理常式就是把核心訊息傳給syslogd守護進程。   syslogd守護進程一般把接收到的訊息寫入/var/log/messages檔案中。不過,還是可以通過/etc/syslog.conf檔案來進行配置,可以選擇其他的輸出檔案。  圖1 X光了此過程:   

四、OOPS OOPS(也稱 Panic)訊息包含系統錯誤的細節,如 CPU 寄存器的內容等。是核心告知使用者有不幸發生的最常用的方式。 核心只能發布OOPS,這個過程包括向終端上輸出錯誤訊息,輸出寄存器儲存的資訊,並輸出可供跟蹤的回溯線索。通常,發送完OOPS之後,核心會處於一種不穩定的狀態。 OOPS的產生有很多可能原因,其中包括記憶體訪問越界或非法的指令等。 ※ 作為核心的開發人員,必定將會經常處理OOPS。 ※ OOPS中包含的重要訊息,對所有體繫結構的機器都是完全相同的: 寄存器上下文和回溯線索(回溯線索顯示了導致錯誤發生的函數調用鏈 。  1、ksymoops    在 Linux 中,調試系統崩潰的傳統方法是分析在發生崩潰時發送到系統控制台的 Oops 訊息。一旦您掌握了細節,就可以將訊息發送到 ksymoops 公用程式,它將試圖將代碼轉換為指令並將堆棧值對應到核心符號。    ※ 如:回溯線索中的地址,會通過ksymoops轉化成名稱可見的函數名。    圖2X光了格式化 Oops 訊息過程:    

    ksymoops需要幾項內容:Oops 訊息輸出、來自正在啟動並執行核心的 System.map 檔案,還有 /proc/ksyms、vmlinux 和 /proc/modules。關於如何使用 ksymoops,核心原始碼 /usr/src/linux/Documentation/oops-tracing.txt 中或 ksymoops 手冊頁上有完整的說明可以參考。Ksymoops 反組譯碼代碼部分,指出發生錯誤的指令,並顯示一個跟蹤部分表明代碼如何被調用。  2、kallsyms    開發版2.5核心引入了kallsyms特性,它可以通過定義CONFIG_KALLSYMS編譯選項啟用。該選項可以載入 核心鏡像所對應的記憶體位址的符號名稱(即函數名),所以核心可以列印解碼之後的跟蹤線索。相應,解碼OOPS也不再需要System.map和ksymoops工具了。另外,這樣做,會使核心變大些,因為地址對應符號名稱必須始終駐留在核心所在記憶體上。    #cat /proc/kallsyms     c0100240   T       _stext     c0100240   t       run_init_process     c0100240   T      stext     c0100269   t       init        …  五、核心調試配置選項 在編譯核心的時候,為了方便調試和測試代碼,核心提供了許多配置選項。 ※  啟用選項例如:   slab layer debugging(slab層調試選項)、high-memory debugging(高端記憶體調試選項)、I/O mapping debugging(I/O映射調試選項)、spin-lock debugging(自旋鎖調試選項)、stack-overflow checking(棧溢出檢查選項)和sleep-inside-spinlock checking(自旋鎖內睡眠選項)等。 1、調試原子操作 從核心2.5開發,為了 檢查各類由原子操作引發的問題,核心提供了極佳的工具。 核心提供了一個 原子操作計數器,它可以配置成,一旦在原子操作過程中,進城進入睡眠或者做了一些可能引起睡眠的操作,就列印警告資訊並提供追蹤線索。 所以,包括在 使用鎖的時候調用schedule(),正使用鎖的時候以阻塞方式請求分配記憶體等,各種潛在的bug都能夠被探測到。 下面這些選項可以最大限度地利用該特性: CONFIG_PREEMPT = y CONFIG_DEBUG_KERNEL = y CONFIG_KLLSYMS = y CONFIG_SPINLOCK_SLEEP = y  六、引發bug並列印資訊 1、一些核心調用可以用來方便標記bug,提供斷言並輸出資訊。最常用的兩個是BUG()和BUG_ON()。 定義在<include/asm-generic>中:
#ifndef HAVE_ARCH_BUG
#define BUG() do { 
    printk("BUG: failure at %s:%d/%s()! ", __FILE__, __LINE__, __FUNCTION__); 
    panic("BUG!");   /* 引發更嚴重的錯誤,不但列印錯誤訊息,而且整個系統業會掛起 */
} while (0)
#endif

#ifndef HAVE_ARCH_BUG_ON
    #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)
#endif  當調用這兩個宏的時候,它們會引發OOPS,導致棧的回溯和錯誤訊息的列印。※ 可以把這兩個調用當作斷言使用,如:BUG_ON(bad_thing);  2、dump_stack()    有些時候,只需要在終端上列印一下棧的回溯資訊來協助你調試。這時可以使用dump_stack()。這個函數只在終端上列印寄存器上下文和函數的跟蹤線索。    if (!debug_check) {        printk(KERN_DEBUG “provide some information…/n”);        dump_stack();    } 備忘:大部分內容引自《Linux核心設計與實現 - 第2版》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.