## 開篇 現在流行的開源項目經歷了長時間的開發, 積累了大量的代碼, 想要一行一行地閱讀代碼去學習開源項目, 需要的時間成本是巨大的. 所以, 我們也需要用一種高效的方式去"閱讀"代碼. 電腦科學發展到現在, 產生了很多高效成熟的工具, 調試器就是其中之一(扯句題外話, 那些大牛程式員似乎就是喜 ...
開篇
現在流行的開源項目經歷了長時間的開發, 積累了大量的代碼, 想要一行一行地閱讀代碼去學習開源項目, 需要的時間成本是巨大的. 所以, 我們也需要用一種高效的方式去"閱讀"代碼. 電腦科學發展到現在, 產生了很多高效成熟的工具, 調試器就是其中之一(扯句題外話, 那些大牛程式員似乎就是喜歡琢磨怎麼製造各種工具哈), 調試器能夠幫程式員定位代碼的bug, 理解代碼的運行機制. 這篇文章總結瞭如何搭建一個調試linux內核源碼的開發環境, 並簡單介紹了一些調試器的實現原理.
問題1
調試內核和調試用戶態程式有什麼區別?
問題2
使用qemu調試內核, 原理是什麼?
問題3
如何搭建調試linux內核源碼的開發環境?
回答1
我們把討論限定在使用c語言寫的程式上, 一個程式想在linux運行, 大致會經過下麵幾個步驟:
- 你首先會編譯源代碼得到一個ELF文件, 這個文件中包含了用戶程式的二進位指令和數據, 它具有可執行的許可權;
- 接下來你在shell中運行這個ELF文件, shell會使用fork, exec等系統調用生成一個子進程去運行你的程式, 系統調用進入內核之後, 編譯得到的ELF文件被用來構造這個子進程在記憶體中的初始狀態, 內核還需要為這個進程返回用戶態之後的寄存器設置合適的初始狀態. 可以說, 一個進程就像一個狀態機, 它的狀態包括記憶體和寄存器的狀態, 在這一步內核對這個狀態機作了初始化;
- 子進程得到調度, 它從內核態返回用戶態, 每執行一條指令, 伴隨的是記憶體和寄存器狀態的變化, 也就是狀態機的狀態轉移;
以上是用戶態進程的運行過程, 如果想調試一個用戶態程式, 可以使用gdb工具, gdb之所以能夠調試程式, 是藉助了內核的幫助. 如果理解了進程是一個狀態機這樣的觀點, 理解gdb調試程式的原理就比較容易了, 調試程式無非就是運行幾條指令,觀察或者修改一下記憶體和寄存器的狀態. 內核提供了ptrace系統調用, 可以讓一個進程去"跟蹤(trace)"另一個進程的狀態, 使用ptrace系統調用, gdb可以修改被調試進程的記憶體和寄存器, 比如修改原來記憶體中的某條指令為另一條特殊的指令, 就可以實現"打斷點"的功能, 具體的細節會在其他文章中討論, 手寫一個簡單的調試器體會一下ptrace系統調用.
調試用戶態程式依賴內核提供的系統調用, 但是調試內核本身, 就不能再依賴它本身提供的系統調用了, 我們需要一種模擬器軟體, 也就是接下來要介紹的qemu.
回答2
qemu是一種模擬器軟體, 用軟體的方式模擬了一整個硬體平臺, 支持不同類型的CPU架構, 眾多的IO設備. 使用qmeu, 可以運行完整的linux操作系統, 通過qemu的啟動參數可以方便地添加修改硬體的配置, 比如CPU的數量, 增加硬碟, 網卡等設備. 對於調試內核來說, 我們需要把編譯好的內核鏡像傳遞給它, 依賴qemu提供的gdb調試服務支持, 改變啟動參數, 可以讓qemu啟動內核的同時啟動一個gdb server服務, 在特定的埠上等待gdb client的連接, 並執行gdb client的命令.
以上就是使用qemu調試內核的基本原理. 從操作系統的角度看, qemu也是一個普通的用戶進程, 只不過它可以模擬一臺真實的機器, 並且能夠接受另外的gdb client進程的命令, 從而控制其中運行的linux內核的執行. 這個gdb client進程既可以和qemu在統一臺機器上運行, 也可以通過網路連接到qemu的gdb server服務, 使用起來比較方便.
回答3
以下均在ubuntu系統上完成, 需要準備的內容包括:
- 交叉編譯工具鏈
- 這裡使用arm或者riscv平臺
- 交叉編譯工具用來編譯linux內核源碼和busybox源碼
- linux內核源碼
- 可以使用5.4或者之後的版本
- 編譯得到內核鏡像文件, 通過啟動參數傳遞給qemu
- busybox源碼
- 可以使用1.36或者之後的版本
- 編譯之後用於製作根文件系統, 供內核使用
- 安裝好的qemu
- 啟動linux內核
- 等待gdb的連接
- 其他軟體
- gdb-multiarch
- guestfs-tools
- 其他
具體的調試環境準備可以參考下麵的視頻:
結尾
按照視頻的步驟操作下來, 實現調試linux內核應該不會有什麼問題. 但是qemu的一些啟動參數, 傳遞給內核的啟動參數, 以及這些參數為什麼要這樣設置, 是否可以改成其他的值, 可能還是會有疑惑的. 我的建議是, 先把環境搞起來, 等對qemu和內核源碼熟悉一點了, 很多問題自然就明白了.
- 比如你想知道內核的啟動參數有什麼用? 就可以通過調試代碼的方式, 找到內核解析啟動參數的部分, 分析出它在做什麼.
- 對於qmeu的問題, 最好是找官方文檔, 這裡放個鏈接, 每個參數的含義在文檔中都可以找到.
- 文檔中沒說清楚的, 你可以把qemu的源碼拿來看, 自己編譯一下, 然後用gdb調試qemu, 並且在qemu啟動時加上 -s -S參數, 這個時候, 你就成功實現了一個兩層的gdb套娃, qemu在被調試, 同時qemu里的內核也在被調試, 如果你願意, 你還可以在qemu虛擬機裡面在起一個gdb去調試用戶進程, 這樣就得到了一個三層的gdb套娃.
- 三層gdb套娃不是為了炫技, 這能夠幫你在更高的層次上理解這一整套軟體棧時如何協同工作的. 比如你在qemu虛擬機里的進程訪問了一個硬體設備, 會觸發到qemu中的運行的linux內核的驅動代碼, 驅動代碼的執行, 又會觸發到qemu對某個設備的模擬代碼上, 有了三層gdb套娃, 你就有機會瞭解你想知道的任何實現細節, 從而幫助你解決遇到的各種問題.
- 以上是一些學習和工作經驗的總結, 詳細的討論會在其他文章中展開, 這裡先挖個坑.
總而言之, 源碼之下沒有秘密. 讀文檔, 讀源碼能夠解決你的大部分問題.
建了個QQ群: 838923389. 有想法的老鐵可以加一下, 一起交流linux內核的使用和學習經驗. 後續也會在b站發一些技術視頻, 老鐵們覺得有需要可以先關註一下, 視頻和文章肯定會給各位帶來一些啟發和幫助. 更多文章還可以參考我的csdn.
本文來自博客園,作者:kfggww,轉載請註明原文鏈接:https://www.cnblogs.com/kfggww/p/17614251.html