《linux內核設計與實現》第十八章

第十八章 調試git

調試工做艱難是內核級開發區別於用戶級開發的一個顯著特色。算法

1、準備開始架構

  一、內和調試須要什麼函數

  • 一個bug(大部分bug一般都不是行爲可靠並且定義明確的)
  • 一個藏匿bug的內核版本(知道bug最先出如今哪一個內核版本中)
  • 相關內核代碼的知識和運氣(加深理解周圍的代碼)

  若是錯誤老是可以重現的話,消除錯誤的機會會很大。oop

2、內核中的bug性能

  bug發做時可能的症狀:學習

  • 明白無誤的錯誤代碼(好比,沒有把正確的值存放在恰當的位置)
  • 同步時發生的錯誤(好比共享變量鎖定不當)
  • 錯誤地管理硬件(好比,給錯誤的控制寄存器發送錯誤的指令)
  • 從下降全部程序的運行性能到毀壞數據再到使得系統處於死鎖狀態

3、經過打印來調試測試

  printk()就是內核的格式化打印函數ui

  一、健壯性spa

  健壯性是printk()函數最容易讓人們接受的一個特質。任什麼時候候,任何地方都能調用它,內核中的printk()比比皆是。

  • 能夠在中斷上下文和進程上下中被調用
  • 能夠在任何持有鎖時被調用
  • 能夠在多處理器上同時被調用,並且調用者連鎖都沒必要使用

  負責執行硬件體系結構相關的初始化動做的函數是:setup_arch()

  二、日誌等級

  printk()和printf()在使用上最主要的區別就是前者能夠指定一個日誌級別。

  內核根據這個級別來判斷是否在終端上打印消息。

  內核把級別比某個特定值低的全部消息顯示在終端上。

  可供使用的記錄等級:

 

 

  默認等級:KERN_WARNING

  三、記錄緩衝區

  • 內核消息都被保存在一個LOG_BUF_LEN大小的環形隊列中。
  • 該緩衝區大小能夠在編譯時經過設置CONFIG_LOG_BUF_SHIFT進行調整。
  • 在單處理器的系統上其默認值是16KB。(內核在同一時間只能保存16KB的內核消息)
  • 若是消息隊列已經達到最大值,那麼若是再有printk()調用時,新消息將覆蓋隊列中的老消息
  • 這個記錄緩衝區之因此稱爲環形是由於它的讀寫都是按照環形隊列方式進行操做的

  環形隊列的優缺點:

  優勢:

  • 因爲同時讀寫環形緩衝區時,其同步問題很容易解決,因此即便在中斷上下文中也能夠方便地使用printfk()
  • 它使記錄維護起來也更容易。若是有大量的消息同時產生,新消息只需覆蓋掉舊消息便可。
  • 在某個問題引起大量消息的時候。記錄只會覆蓋掉它自己,而不會由於失控而消耗掉大量內存。

  缺點:

  • 可能會丟失消息

  四、syslogd和klogd

  • 用戶空間的守護進程klogd從記錄緩衝區中獲取內核消息,再經過syslogd守護進程將它們保存在系統日誌文件中。
  • klogd程序既能夠從/proc/kmsg文件中,也能夠經過syslog()系統調用讀取這些消息
  • 默認狀況下,它選擇讀取/proc方式實現,不論是哪一種方法,klogd都會阻塞,直到有新的內核消息可供讀出。在被喚醒以後,它會讀取出新的內核消息並進行處理,默認狀況下,它就是把消息傳給syslogd守護進程
  • syslogd守護進程把它接收到的全部消息添加進一個文件中,該文件默認是/va也r/log/messages。能夠經過配置文件從新指定
  • 在啓動klogd的時候,能夠經過指定-c標誌來改變終端的記錄等級。

4、oops

  oops是內核告知用戶有不行法神最經常使用的方式。

  oops中包含的重要信息對於全部體系結構都是徹底相同的:寄存器上下文和回溯線索

  是一個通過解碼的oops,由於內存地址都已經裝換成了對應的函數。

  • 回溯線索顯示了致使錯誤發生的函數調用鏈。

  一、ksymoops

  回溯線索中的地址須要轉化成有意義的符號名稱才方便使用,這須要調用ksymoops命令。而且還必須提供編譯內核時產生的System.map。若是使用的是模塊,還須要一些模塊信息。

  而後該程序就會吐出解碼版的oops。若是ksymoops沒法找到默認位置上的信息,或者想提供不一樣信息,該程序能夠接受許多參數。

  二、kallsyms

  配置選項CONFIG_KALLSYMS_ALL 表示不只存放函數名稱,還存放全部的符號名稱。

5、內核調試配置選項

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

6、引起bug並打印信息

  一些內核調用能夠用來方便標記bug方便標記bug提供斷言並輸出信息。

  最經常使用的兩個是BUG()和些聲明BUG_ON()。當被調用的時候,它們會引起oops,致使棧的回溯和錯誤信息的打印。

7、神奇的系統請求鍵

  • 該功能能夠經過定義CONFIG_MAGIC_SYSRQ配置選項來啓用。
  • 當該功能被啓用的時候,不管內核處於什麼狀態,均可以經過特殊的組合鍵跟內核進行通訊。
  • 除了配置選項之外,還要經過一個sysctl用來標記該特性的開或關。

  啓用這個鍵的功能有2個方法:
  開啓內核編譯選項 : CONFIG_MAGIC_SYSRQ
  動態啓用: echo 1 > /proc/sys/kernel/sysrq

 

  支持sysrq的命令:

 

8、內核調試器的傳奇

一、gdb

二、kgdb

  kgdb是一個補丁,它可讓咱們在遠端主機上經過串口利用gdb的全部功能對內核進行調試。

  這須要兩臺計算機:第一臺運行帶有kgdb補丁的內核,第二臺經過串行線使用gdb對第一臺進行調試。

  經過kgdb的全部功能都能使用:讀取或修改變量值,設置斷點,設置關注變量,單步執行等。某些版本的gdb甚至容許執行函數。

9、探測系統

若是對內核調試有豐富的經驗的話,那麼你會掌握一些訣竅來幫助你更進一步地探測系統從而找到想要的答案。內核調試頗有挑戰性,即便是一點小的暗示或者技巧都能給你很大的幫助咱們最好把它們聯繫起來。

一、用UID做爲選擇條件

  能夠利用把用戶id做爲選擇條件來實現這種功能,經過這種選擇條件,能夠安排到底執行哪一種算法。

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

二、使用條件變量

  • 若是代碼與進程無關,或者但願有一個針對全部狀況都能使用的機制來控制某個特性,可使用條件變量。
  • 只須要建立一個全局變量做爲一個條件選擇開關(若是該變量爲零,就使用一個分支上的代碼。若是它不爲零,就選擇另一個分支)
  • 能夠經過某種接口提供對這個變量的操控,也能夠直接經過調試器進行操控。

三、使用統計量

  當須要掌握某個特定事件的發生規律而且須要比較多個事件並從中得出規律能夠經過建立統計量並提供某種機制訪問其統計結果。

四、重複頻率限制

10、使用Git進行二分搜索

  若是你使用Git來控制Linux源碼樹的副本,那麼Git將自動運行二分搜索進程。

  Git會在修訂版本中進行二分搜索,這樣能夠找到具體哪次提交的代碼引起了bug。

11、小結

  這一章講的是內核的調試。咱們學習了幾種技術:內核內置的調試架構、調試程序、記錄日誌、git二分法查找等。咱們知道了內和調試須要什麼一個bug(大部分bug一般都不是行爲可靠並且定義明確的)、一個藏匿bug的內核版本(知道bug最先出如今哪一個內核版本中)、相關內核代碼的知識和運氣(加深理解周圍的代碼)。以及內核中的bug可能發生的症狀等相關問題。

相關文章
相關標籤/搜索