1. 程式人生 > >Linux 系統核心的除錯

Linux 系統核心的除錯

教您如何搭建Linux核心除錯環境 /*----------------------------------------------------------------------------------------------------------------------------*/

一 安裝linux虛擬機器

1 在vmware上安裝新的linux虛擬機器.建立一個新的虛擬機器,並一定要用IDE硬碟

2 從Internet下載原始碼
1、linux-2.6.18(www.kernel.org -ftp站點)
2、kgdb-2.6.18 patch
3、gdbmod-2.4(可選):除錯module使用此gdb

3 編譯核心
a、將kgdb-2.6.18補丁打到Linux核心原始碼裡。注意按照順序打patch,因為此版本的patch是散的,需要手動一個一個新增,共12個檔案。

b、編譯核心(make clean , make mrproper, make menuconfig, make, make modules , make modules_install, make install)

在核心配置選單的Kernel hacking選項中選擇kgdb除錯項,例如:
[*] KGDB: kernel debugging with remote gdb
[*] KGDB: Console messages through gdb
Method for KGDB communication (KGDB: On generic serial port (8250)) --->
< > KGDB: On ethernet
[*] Simple selection of KGDB serial port
(115200) Debug serial port baud rate
(0) Serial port number for KGDB

在device drivers選擇項中選擇kgdb相關項,例如
Character devices-->
KGDB: On generic serial port(8250)

c、安裝gdbmod-2.4

4 修改grub.conf
例如:
title Fedora Core (2.6.16)
root (hd0,0)
kernel /vmlinuz-2.6.16 ro root=/dev/VolGroup00/LogVol00
initrd /initrd-2.6.16.img

改為:
title Fedora Core (2.6.18)
root (hd0,0)
kernel /vmlinuz-2.6.18 ro root=/dev/VolGroup00/LogVol00 kgdb8250=0,115200 (kgdbwait)
initrd /initrd-2.6.18.img

5 請檢驗新編譯的核心是否可用。如果不能啟動,可能是編譯選項沒有選好。如果沒有問題,關閉這個linux虛擬機器,進入下一步。

二 Clone linux虛擬機器(請參考vmware手冊)

1 Clone linux.在vmware上選擇剛安裝的linux虛擬機器,然後vmware的clone方式。建議選擇連結copy.

2 兩臺虛擬機器:一臺作target機,一臺作develop機。

3 配置兩臺虛擬機器序列通訊
a、在每個虛擬機器中分別新增一個串列埠裝置,並配置成命名管道

b、指定target虛擬機器的串列埠為server端,並選擇"The other end is a virtual machine"屬性,對於IO mode屬性,選中"Yield CPU on poll"複選擇框;

c、指定develop虛擬機器的串列埠為client端,並選擇"The other end is a virtual machine"屬性。

4 注意:這裡的delelop的Linux虛擬機器不要使用打了kgdb patch的linux系統,否則無法配置序列口。因為kgdb佔用了ttyS0口,禁止使用。我這裡使用的是2.6.16核心的系統。

三 除錯核心(以除錯網路功能為例)
1、啟動target虛擬機器

2、在develop虛擬機器的linux-2.6.18原始碼目錄下,執行下面的命令:
gdb ./vmlinux
(gdb) set remotebaud 115200
(gdb) target remote /dev/ttyS0
(gdb) break ip_rcv
(gdb) c
3、給target 虛擬機發資料包(如ping)

4、現在就像除錯應用程式那樣除錯核心了。
5、kgdb-2.6.18網站上有,但不是打包的,分散的。需要一個個按照順序Pack,順序不能錯,否則核心編譯不過。

轉自http://www.ibm.com/developerworks/cn/linux/l-kdb/index.html

 除錯是軟體開發過程中一個必不可少的環節,在 Linux 核心開發的過程中也不可避免地會面對如何除錯核心的問題。但是,Linux 系統的開發者出於保證核心程式碼正確性的考慮,不願意在 Linux 核心原始碼樹中加入一個偵錯程式。他們認為核心中的偵錯程式會誤導開發者,從而引入不良的修正[1]。所以對 Linux 核心進行除錯一直是個令核心程式設計師感到棘手的問題,除錯工作的艱苦性是核心級的開發區別於使用者級開發的一個顯著特點。

儘管缺乏一種內建的除錯核心的有效方法,但是 Linux 系統在核心發展的過程中也逐漸形成了一些監視核心程式碼和錯誤跟蹤的技術。同時,許多的補丁程式應運而生,它們為標準核心附加了核心除錯的支援。儘管這些補丁有些並不被 Linux 官方組織認可,但他們確實功能完善,十分強大。除錯核心問題時,利用這些工具與方法跟蹤核心執行情況,並檢視其記憶體和資料結構將是非常有用的。

本文將首先介紹 Linux 核心上的一些核心程式碼監視和錯誤跟蹤技術,這些除錯和跟蹤方法因所要求的使用環境和使用方法而各有不同,然後重點介紹三種 Linux 核心的原始碼級的除錯方法。

printk() 是除錯核心程式碼時最常用的一種技術。在核心程式碼中的特定位置加入printk() 除錯呼叫,可以直接把所關心的資訊打列印到螢幕上,從而可以觀察程式的執行路徑和所關心的變數、指標等資訊。 Linux 核心偵錯程式(Linux kernel debugger,kdb)是 Linux 核心的補丁,它提供了一種在系統能執行時對核心記憶體和資料結構進行檢查的辦法。Oops、KDB在文章掌握 Linux 除錯技術有詳細介紹,大家可以參考。 Kprobes 提供了一個強行進入任何核心例程,並從中斷處理器無干擾地收集資訊的介面。使用 Kprobes 可以輕鬆地收集處理器暫存器和全域性資料結構等除錯資訊,而無需對Linux核心頻繁編譯和啟動,具體使用方法,請參考使用 Kprobes 除錯核心

以上介紹了進行Linux核心除錯和跟蹤時的常用技術和方法。當然,核心除錯與跟蹤的方法還不止以上提到的這些。這些除錯技術的一個共同的特點在於,他們都不能提供原始碼級的有效的核心除錯手段,有些只能稱之為錯誤跟蹤技術,因此這些方法都只能提供有限的除錯能力。下面將介紹三種實用的原始碼級的核心除錯方法。

kgdb提供了一種使用 gdb除錯 Linux 核心的機制。使用KGDB可以象除錯普通的應用程式那樣,在核心中進行設定斷點、檢查變數值、單步跟蹤程式執行等操作。使用KGDB除錯時需要兩臺機器,一臺作為開發機(Development Machine),另一臺作為目標機(Target Machine),兩臺機器之間通過串列埠或者乙太網口相連。串列埠連線線是一根RS-232介面的電纜,在其內部兩端的第2腳(TXD)與第3腳(RXD)交叉相連,第7腳(接地腳)直接相連。除錯過程中,被除錯的核心執行在目標機上,gdb偵錯程式執行在開發機上。

目前,kgdb釋出支援i386、x86_64、32-bit PPC、SPARC等幾種體系結構的偵錯程式。有關kgdb補丁的下載地址見參考資料[4]。

安裝kgdb除錯環境需要為Linux核心應用kgdb補丁,補丁實現的gdb遠端除錯所需要的功能包括命令處理、陷阱處理及串列埠通訊3個主要的部分。kgdb補丁的主要作用是在Linux核心中添加了一個除錯Stub。除錯Stub是Linux核心中的一小段程式碼,提供了執行gdb的開發機和所除錯核心之間的一個媒介。gdb和除錯stub之間通過gdb序列協議進行通訊。gdb序列協議是一種基於訊息的ASCII碼協議,包含了各種除錯命令。當設定斷點時,kgdb負責在設定斷點的指令前增加一條trap指令,當執行到斷點時控制權就轉移到除錯stub中去。此時,除錯stub的任務就是使用遠端序列通訊協議將當前環境傳送給gdb,然後從gdb處接受命令。gdb命令告訴stub下一步該做什麼,當stub收到繼續執行的命令時,將恢復程式的執行環境,把對CPU的控制權重新交還給核心。



下面我們將以Linux 2.6.7核心為例詳細介紹kgdb除錯環境的建立過程。

2.2.1軟硬體準備

以下軟硬體配置取自筆者進行試驗的系統配置情況:



kgdb補丁的版本遵循如下命名模式:Linux-A-kgdb-B,其中A表示Linux的核心版本號,B為kgdb的版本號。以試驗使用的kgdb補丁為例,linux核心的版本為linux-2.6.7,補丁版本為kgdb-2.2。

物理連線好串列埠線後,使用以下命令來測試兩臺機器之間串列埠連線情況,stty命令可以對串列埠引數進行設定:

在development機上執行:


stty ispeed 115200 ospeed 115200 -F /dev/ttyS0

在target機上執行:


stty ispeed 115200 ospeed 115200 -F /dev/ttyS0

在developement機上執行:


echo hello > /dev/ttyS0

在target機上執行:


cat /dev/ttyS0

如果串列埠連線沒問題的話在將在target機的螢幕上顯示"hello"。

2.2.2 安裝與配置

下面我們需要應用kgdb補丁到Linux核心,設定核心選項並編譯核心。這方面的資料相對較少,筆者這裡給出詳細的介紹。下面的工作在開發機(developement)上進行,以上面介紹的試驗環境為例,某些具體步驟在實際的環境中可能要做適當的改動:

I、核心的配置與編譯


[[email protected] tmp]# tar -jxvf linux-2.6.7.tar.bz2
[[email protected] tmp]#tar -jxvf linux-2.6.7-kgdb-2.2.tar.tar
[[email protected] tmp]#cd inux-2.6.7

請參照目錄補丁包中檔案README給出的說明,執行對應體系結構的補丁程式。由於試驗在i386體系結構上完成,所以只需要安裝一下補丁:core-lite.patch、i386-lite.patch、8250.patch、eth.patch、core.patch、i386.patch。應用補丁檔案時,請遵循kgdb軟體包內series檔案所指定的順序,否則可能會帶來預想不到的問題。eth.patch檔案是選擇乙太網口作為除錯的連線埠時需要運用的補丁

應用補丁的命令如下所示:


[[email protected] tmp]#patch -p1 <../linux-2.6.7-kgdb-2.2/core-lite.patch

如果核心正確,那麼應用補丁時應該不會出現任何問題(不會產生*.rej檔案)。為Linux核心添加了補丁之後,需要進行核心的配置。核心的配置可以按照你的習慣選擇配置Linux核心的任意一種方式。


[[email protected] tmp]#make menuconfig

在核心配置選單的Kernel hacking選項中選擇kgdb除錯項,例如:


[*] KGDB: kernel debugging with remote gdb
Method for KGDB communication (KGDB: On generic serial port (8250)) --->
[*] KGDB: Thread analysis
[*] KGDB: Console messages through gdb
[[email protected] tmp]#make

編譯核心之前請注意Linux目錄下Makefile中的優化選項,預設的Linux核心的編譯都以-O2的優化級別進行。在這個優化級別之下,編譯器要對核心中的某些程式碼的執行順序進行改動,所以在除錯時會出現程式執行與程式碼順序不一致的情況。可以把Makefile中的-O2選項改為-O,但不可去掉-O,否則編譯會出問題。為了使編譯後的核心帶有除錯資訊,注意在編譯核心的時候需要加上-g選項。

不過,當選擇"Kernel debugging->Compile the kernel with debug info"選項後配置系統將自動開啟除錯選項。另外,選擇"kernel debugging with remote gdb"後,配置系統將自動開啟"Compile the kernel with debug info"選項。

核心編譯完成後,使用scp命令進行將相關檔案拷貝到target機上(當然也可以使用其它的網路工具,如rcp)。


[[email protected] tmp]#scp arch/i386/boot/bzImage [email protected]:/boot/vmlinuz-2.6.7-kgdb
[[email protected] tmp]#scp System.map [email protected]:/boot/System.map-2.6.7-kgdb

如果系統啟動使所需要的某些裝置驅動沒有編譯進核心的情況下,那麼還需要執行如下操作:


[[email protected] tmp]#mkinitrd /boot/initrd-2.6.7-kgdb 2.6.7
[[email protected] tmp]#scp initrd-2.6.7-kgdb [email protected]:/boot/ initrd-2.6.7-kgdb

II、kgdb的啟動

在將編譯出的核心拷貝的到target機器之後,需要配置系統載入程式,加入核心的啟動選項。以下是kgdb核心引導引數的說明:



如表中所述,在kgdb 2.0版本之後核心的引導引數已經與以前的版本有所不同。使用grub載入程式時,直接將kgdb引數作為核心vmlinuz的引導引數。下面給出引導器的配置示例。


title 2.6.7 kgdb
root (hd0,0)
kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait kgdb8250=1,115200

在使用lilo作為載入程式時,需要把kgdb參放在由append修飾的語句中。下面給出使用lilo作為引導器時的配置示例。


image=/boot/vmlinuz-2.6.7-kgdb
label=kgdb
read-only
root=/dev/hda3
append="gdb gdbttyS=1 gdbbaud=115200"

儲存好以上配置後重新啟動計算機,選擇啟動帶除錯資訊的核心,核心將在短暫的執行後在建立init核心執行緒之前停下來,打印出以下資訊,並等待開發機的連線。

Waiting for connection from remote gdb...

在開發機上執行:


gdb
file vmlinux
set remotebaud 115200
target remote /dev/ttyS0

其中vmlinux是指向原始碼目錄下編譯出來的Linux核心檔案的連結,它是沒有經過壓縮的核心檔案,gdb程式從該檔案中得到各種符號地址資訊。

這樣,就與目標機上的kgdb除錯介面建立了聯絡。一旦建立聯接之後,對Linux內的除錯工作與對普通的運用程式的除錯就沒有什麼區別了。任何時候都可以通過鍵入ctrl c打斷目標機的執行,進行具體的除錯工作。

在kgdb 2.0之前的版本中,編譯核心後在arch/i386/kernel目錄下還會生成可執行檔案gdbstart。將該檔案拷貝到target機器的/boot目錄下,此時無需更改核心的啟動配置檔案,直接使用命令:


[[email protected] boot]#gdbstart -s 115200 -t /dev/ttyS0

可以在KGDB核心引導啟動完成後建立開發機與目標機之間的除錯聯絡。

2.2.3 通過網路介面進行除錯

kgdb也支援使用乙太網介面作為偵錯程式的連線埠。在對Linux核心應用補丁包時,需應用eth.patch補丁檔案。配置核心時在Kernel hacking中選擇kgdb除錯項,配置kgdb除錯埠為乙太網介面,例如:


[*]KGDB: kernel debugging with remote gdb
Method for KGDB communication (KGDB: On ethernet) --->
( ) KGDB: On generic serial port (8250)
(X) KGDB: On ethernet

另外使用eth0網口作為除錯埠時,grub.list的配置如下:


title 2.6.7 kgdb
root (hd0,0)
kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait [email protected]
5.13/,@192.168. 6.13/

其他的過程與使用串列埠作為連線埠時的設定過程相同。

注意:儘管可以使用乙太網口作為kgdb的除錯埠,使用串列埠作為連線埠更加簡單易行,kgdb專案組推薦使用串列埠作為除錯埠。

2.2.4 模組的除錯方法

核心可載入模組的除錯具有其特殊性。由於核心模組中各段的地址是在模組載入進核心的時候才最終確定的,所以develop機的gdb無法得到各種符號地址資訊。所以,使用kgdb除錯模組所需要解決的一個問題是,需要通過某種方法獲得可載入模組的最終載入地址資訊,並把這些資訊加入到gdb環境中。

I、在Linux 2.4核心中的核心模組除錯方法

在Linux2.4.x核心中,可以使用insmod -m命令輸出模組的載入資訊,例如:


[[email protected] tmp]# insmod -m hello.ko >modaddr

檢視模組載入資訊檔案modaddr如下:


.this 00000060 c88d8000 2**2
.text 00000035 c88d8060 2**2
.rodata 00000069 c88d80a0 2**5
……
.data 00000000 c88d833c 2**2
.bss 00000000 c88d833c 2**2
……

在這些資訊中,我們關心的只有4個段的地址:.text、.rodata、.data、.bss。在development機上將以上地址資訊加入到gdb中,這樣就可以進行模組功能的測試了。


(gdb) Add-symbol-file hello.o 0xc88d8060 -s .data 0xc88d80a0 -s
.rodata 0xc88d80a0 -s .bss 0x c88d833c

這種方法也存在一定的不足,它不能除錯模組初始化的程式碼,因為此時模組初始化程式碼已經執行過了。而如果不執行模組的載入又無法獲得模組插入地址,更不可能在模組初始化之前設定斷點了。對於這種除錯要求可以採用以下替代方法。

在target機上用上述方法得到模組載入的地址資訊,然後再用rmmod解除安裝模組。在development機上將得到的模組地址資訊匯入到gdb環境中,在核心程式碼的呼叫初始化程式碼之前設定斷點。這樣,在target機上再次插入模組時,程式碼將在執行模組初始化之前停下來,這樣就可以使用gdb命令除錯模組初始化程式碼了。

另外一種除錯模組初始化函式的方法是:當插入核心模組時,核心模組機制將呼叫函式sys_init_module(kernel/modle.c)執行對核心模組的初始化,該函式將呼叫所插入模組的初始化函式。程式程式碼片斷如下:


…… ……
if (mod->init != NULL)
ret = mod->init();
…… ……

在該語句上設定斷點,也能在執行模組初始化之前停下來。

II、在Linux 2.6.x核心中的核心模組除錯方法

Linux 2.6之後的核心中,由於module-init-tools工具的更改,insmod命令不再支援-m引數,只有採取其他的方法來獲取模組載入到核心的地址。通過分析ELF檔案格式,我們知道程式中各段的意義如下:

.text(程式碼段):用來存放可執行檔案的操作指令,也就是說是它是可執行程式在記憶體種的映象。

.data(資料段):資料段用來存放可執行檔案中已初始化全域性變數,也就是存放程式靜態分配的變數和全域性變數。

.bss(BSS段):BSS段包含了程式中未初始化全域性變數,在記憶體中 bss段全部置零。

.rodata(只讀段):該段儲存著只讀資料,在程序映象中構造不可寫的段。

通過在模組初始化函式中放置一下程式碼,我們可以很容易地獲得模組載入到記憶體中的地址。


……
int bss_var;
static int hello_init(void)
{
printk(KERN_ALERT "Text location .text(Code Segment):%p\n",hello_init);
static int data_var=0;
printk(KERN_ALERT "Data Location .data(Data Segment):%p\n",&data_var);
printk(KERN_ALERT "BSS Location: .bss(BSS Segment):%p\n",&bss_var);
……
}
Module_init(hello_init);

這裡,通過在模組的初始化函式中新增一段簡單的程式,使模組在載入時打印出在核心中的載入地址。.rodata段的地址可以通過執行命令readelf -e hello.ko,取得.rodata在檔案中的偏移量並加上段的align值得出。

為了使讀者能夠更好地進行模組的除錯,kgdb專案還發布了一些指令碼程式能夠自動探測模組的插入並自動更新gdb中模組的符號資訊。這些指令碼程式的工作原理與前面解釋的工作過程相似,更多的資訊請閱讀參考資料[4]。

2.2.5 硬體斷點

kgdb提供對硬體除錯暫存器的支援。在kgdb中可以設定三種硬體斷點:執行斷點(Execution Breakpoint)、寫斷點(Write Breakpoint)、訪問斷點(Access Breakpoint)但不支援I/O訪問的斷點。目前,kgdb對硬體斷點的支援是通過巨集來實現的,最多可以設定4個硬體斷點,這些巨集的用法如下:



在有些情況下,硬體斷點的使用對於核心的除錯是非常方便的。有關硬體斷點的定義和具體的使用說明見參考資料[4]

kgdb除錯環境需要使用兩臺微機分別充當development機和target機,使用VMware後我們只使用一臺計算機就可以順利完成kgdb除錯環境的搭建。以windows下的環境為例,建立兩臺虛擬機器,一臺作為開發機,一臺作為目標機。

2.3.1虛擬機器之間的串列埠連線

虛擬機器中的串列埠連線可以採用兩種方法。一種是指定虛擬機器的串列埠連線到實際的COM上,例如開發機連線到COM1,目標機連線到COM2,然後把兩個串列埠通過串列埠線相連線。另一種更為簡便的方法是:在較高一些版本的VMware中都支援把串列埠對映到命名管道,把兩個虛擬機器的串列埠對映到同一個命名管道。例如,在兩個虛擬機器中都選定同一個命名管道\\.\pipe\com_1,指定target機的COM口為server端,並選擇"The other end is a virtual machine"屬性;指定development機的COM口端為client端,同樣指定COM口的"The other end is a virtual machine"屬性。對於IO mode屬性,在target上選中"Yield CPU on poll"複選擇框,development機不選。這樣,可以無需附加任何硬體,利用虛擬機器就可以搭建kgdb除錯環境。即降低了使用kgdb進行除錯的硬體要求,也簡化了建立除錯環境的過程。



2.3.2 VMware的使用技巧

VMware虛擬機器是比較佔用資源的,尤其是象上面那樣在Windows中使用兩臺虛擬機器。因此,最好為系統配備512M以上的記憶體,每臺虛擬機器至少分配128M的記憶體。這樣的硬體要求,對目前主流配置的PC而言並不是過高的要求。出於系統性能的考慮,在VMware中儘量使用字元介面進行除錯工作。同時,Linux系統預設情況下開啟了sshd服務,建議使用SecureCRT登陸到Linux進行操作,這樣可以有較好的使用者使用介面。

2.3.3 在Linux下的虛擬機器中使用kgdb

對於在Linux下面使用VMware虛擬機器的情況,筆者沒有做過實際的探索。從原理上而言,只需要在Linux下只要建立一臺虛擬機器作為target機,開發機的工作可以在實際的Linux環境中進行,搭建除錯環境的過程與上面所述的過程類似。由於只需要建立一臺虛擬機器,所以使用Linux下的虛擬機器搭建kgdb除錯環境對系統性能的要求較低。(vmware已經推出了Linux下的版本)還可以在development機上配合使用一些其他的除錯工具,例如功能更強大的cgdb、圖形介面的DDD偵錯程式等,以方便核心的除錯工作。



使用kgdb作為核心除錯環境最大的不足在於對kgdb硬體環境的要求較高,必須使用兩臺計算機分別作為target和development機。儘管使用虛擬機器的方法可以只用一臺PC即能搭建除錯環境,但是對系統其他方面的效能也提出了一定的要求,同時也增加了搭建除錯環境時複雜程度。另外,kgdb核心的編譯、配置也比較複雜,需要一定的技巧,筆者當時做的時候也是費了很多周折。當除錯過程結束後時,還需要重新制作所要釋出的核心。使用kgdb並不能進行全程除錯,也就是說kgdb並不能用於除錯系統一開始的初始化引導過程。

不過,kgdb是一個不錯的核心除錯工具,使用它可以進行對核心的全面除錯,甚至可以除錯核心的中斷處理程式。如果在一些圖形化的開發工具的幫助下,對核心的除錯將更方便。

SkyEye是一個開源軟體專案(OPenSource Software),SkyEye專案的目標是在通用的Linux和Windows平臺上模擬常見的嵌入式計算機系統。SkyEye實現了一個指令級的硬體模擬平臺,可以模擬多種嵌入式開發板,支援多種CPU指令集。SkyEye 的核心是 GNU 的 gdb 專案,它把gdb和 ARM Simulator很好地結合在了一起。加入ARMulator 的功能之後,它就可以來模擬嵌入式開發板,在它上面不僅可以除錯硬體驅動,還可以除錯作業系統。Skyeye專案目前已經在嵌入式系統開發領域得到了很大的推廣。

3.1.1 SkyEye的安裝

SkyEye的安裝不是本文要介紹的重點,目前已經有大量的資料對此進行了介紹。有關SkyEye的安裝與使用的內容請查閱參考資料[11]。由於skyeye面目主要用於嵌入式系統領域,所以在skyeye上經常使用的是μcLinux系統,當然使用Linux作為skyeye上執行的系統也是可以的。由於介紹μcLinux 2.6在skyeye上編譯的相關資料並不多,所以下面進行詳細介紹。

3.1.2 μcLinux 2.6.x的編譯

要在SkyEye中除錯作業系統核心,首先必須使被除錯核心能在SkyEye所模擬的開發板上正確執行。因此,正確編譯待除錯作業系統核心並配置SkyEye是進行核心除錯的第一步。下面我們以SkyEye模擬基於Atmel AT91X40的開發板,並執行μcLinux 2.6為例介紹SkyEye的具體除錯方法。

I、安裝交叉編譯環境

先安裝交叉編譯器。儘管在一些資料中說明使用工具鏈arm-elf-tools-20040427.sh ,但是由於arm-elf-xxx與arm-linux-xxx對巨集及連結處理的不同,經驗證明使用arm-elf-xxx工具鏈在連結vmlinux的最後階段將會出錯。所以這裡我們使用的交叉編譯工具鏈是:arm-uclinux-tools-base-gcc3.4.0-20040713.sh,關於該交叉編譯工具鏈的下載地址請參見[6]。注意以下步驟最好用root使用者來執行。


[[email protected] tmp]#chmod x arm-uclinux-tools-base-gcc3.4.0-20040713.sh
[[email protected] tmp]#./arm-uclinux-tools-base-gcc3.4.0-20040713.sh

安裝交叉編譯工具鏈之後,請確保工具鏈安裝路徑存在於系統PATH變數中。

II、製作μcLinux核心

得到μcLinux釋出包的一個最容易的方法是直接訪問uClinux.org站點[7]。該站點發布的核心版本可能不是最新的,但你能找到一個最新的μcLinux補丁以及找一個對應的Linux核心版本來製作一個最新的μcLinux核心。這裡,將使用這種方法來製作最新的μcLinux核心。目前(筆者記錄編寫此文章時),所能得到的釋出包的最新版本是uClinux-dist.20041215.tar.gz。

下載uClinux-dist.20041215.tar.gz,檔案的下載地址請參見[7]。

下載linux-2.6.9-hsc0.patch.gz,檔案的下載地址請參見[8]。

下載linux-2.6.9.tar.bz2,檔案的下載地址請參見[9]。

現在我們得到了整個的linux-2.6.9原始碼,以及所需的核心補丁。請準備一個有2GB空間的目錄裡來完成以下製作μcLinux核心的過程。


[[email protected] tmp]# tar -jxvf uClinux-dist-20041215.tar.bz2
[[email protected] uClinux-dist]# tar -jxvf linux-2.6.9.tar.bz2
[[email protected] uClinux-dist]# gzip -dc linux-2.6.9-hsc0.patch.gz | patch -p0

或者使用:


[[email protected] uClinux-dist]# gunzip linux-2.6.9-hsc0.patch.gz
[[email protected] uClinux-dist]patch -p0 < linux-2.6.9-hsc0.patch

執行以上過程後,將在linux-2.6.9/arch目錄下生成一個補丁目錄-armnommu。刪除原來μcLinux目錄裡的linux-2.6.x(即那個linux-2.6.9-uc0),並將我們打好補丁的Linux核心目錄更名為linux-2.6.x。


[[email protected] uClinux-dist]# rm -rf linux-2.6.x/
[[email protected] uClinux-dist]# mv linux-2.6.9 linux-2.6.x

III、配置和編譯μcLinux核心

因為只是出於除錯μcLinux核心的目的,這裡沒有生成uClibc庫檔案及romfs.img檔案。在釋出μcLinux時,已經預置了某些常用嵌入式開發板的配置檔案,因此這裡直接使用這些配置檔案,過程如下:


[[email protected] uClinux-dist]# cd linux-2.6.x
[[email protected] linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux- atmel_
deconfig

atmel_deconfig檔案是μcLinux釋出時提供的一個配置檔案,存放於目錄linux-2.6.x /arch/armnommu/configs/中。


[[email protected] linux-2.6.x]#make ARCH=armnommu CROSS_COMPILE=arm-uclinux-
oldconfig

下面編譯配置好的核心:


[[email protected] linux-2.6.x]# make ARCH=armnommu CROSS_COMPILE=arm-uclinux- v=1

一般情況下,編譯將順利結束並在Linux-2.6.x/目錄下生成未經壓縮的μcLinux核心檔案vmlinux。需要注意的是為了除錯μcLinux核心,需要開啟核心編譯的除錯選項-g,使編譯後的核心帶有除錯資訊。開啟編譯選項的方法可以選擇:

"Kernel debugging->Compile the kernel with debug info"後將自動開啟除錯選項。也可以直接修改linux-2.6.x目錄下的Makefile檔案,為其開啟除錯開關。方法如下:。


CFLAGS = -g

最容易出現的問題是找不到arm-uclinux-gcc命令的錯誤,主要原因是PATH變數中沒有包含arm-uclinux-gcc命令所在目錄。在arm-linux-gcc的預設安裝情況下,它的安裝目錄是/root/bin/arm-linux-tool/,使用以下命令將路徑加到PATH環境變數中。


Export PATH=$PATH:/root/bin/arm-linux-tool/bin

IV、根檔案系統的製作

Linux核心在啟動的時的最後操作之一是載入根檔案系統。根檔案系統中存放了嵌入式系統使用的所有應用程式、庫檔案及其他一些需要用到的服務。出於文章篇幅的考慮,這裡不打算介紹根檔案系統的製作方法,讀者可以查閱一些其他的相關資料。值得注意的是,由配置檔案skyeye.conf指定了裝載到核心中的根檔案系統。

編譯完μcLinux核心後,就可以在SkyEye中除錯該ELF執行檔案格式的核心了。前面已經說過利用SkyEye除錯核心與使用gdb除錯運用程式的方法相同。

需要提醒讀者的是,SkyEye的配置檔案-skyeye.conf記錄了模擬的硬體配置和模擬執行行為。該配置檔案是SkyEye系統中一個及其重要的檔案,很多錯誤和異常情況的發生都和該檔案有關。在安裝配置SkyEye出錯時,請首先檢查該配置檔案然後再進行其他的工作。此時,所有的準備工作已經完成,就可以進行核心的除錯工作了。

在SkyEye中可以進行對Linux系統核心的全程除錯。由於SkyEye目前主要支援基於ARM核心的CPU,因此一般而言需要使用交叉編譯工具編譯待除錯的Linux系統核心。另外,製作SkyEye中使用的核心編譯、配置過程比較複雜、繁瑣。不過,當除錯過程結束後無需重新制作所要釋出的核心。

SkyEye只是對系統硬體進行了一定程度上的模擬,所以在SkyEye與真實硬體環境相比較而言還是有一定的差距,這對一些與硬體緊密相關的除錯可能會有一定的影響,例如驅動程式的除錯。不過對於大部分軟體的除錯,SkyEye已經提供了精度足夠的模擬了。

SkyEye的下一個目標是和eclipse結合,有了圖形介面,能為除錯和檢視原始碼提供一些方便。

User-mode Linux(UML)簡單說來就是在Linux內執行的Linux。該專案是使Linux核心成為一個執行在 Linux 系統之上單獨的、使用者空間的程序。UML並不是執行在某種新的硬體體系結構之上,而是執行在基於 Linux 系統呼叫介面所實現的虛擬機器。正是由於UML是一個將Linux作為使用者空間程序執行的特性,可以使用UML來進行作業系統核心的除錯。有關UML的介紹請查閱參考資料[10]、[12]。

UML的安裝需要一臺執行Linux 2.2.15以上,或者2.3.22以上的I386機器。對於2.6.8及其以前版本的UML,採用兩種形式釋出:一種是以RPM包的形式釋出,一種是以原始碼的形式提供UML的安裝。按照UML的說明,以RPM形式提供的安裝包比較陳舊且會有許多問題。以二進位制形式釋出的UML包並不包含所需要的除錯資訊,這些程式碼在釋出時已經做了程度不同的優化。所以,要想利用UML除錯Linux系統核心,需要使用最新的UML patch程式碼和對應版本的Linux核心編譯、安裝UML。完成UML的補丁之後,會在arch目錄下產生一個um目錄,主要的UML程式碼都放在該目錄下。

從2.6.9版本之後(包含2.6.9版本的Linux),User-Mode Linux已經隨Linux核心原始碼樹一起釋出,它存放於arch/um目錄下。

編譯好UML的核心之後,直接使用gdb執行已經編譯好的核心即可進行除錯。

目前,使用者模式 Linux 虛擬機器也存在一定的侷限性。由於UML虛擬機器是基於Linux系統呼叫介面的方式實現的虛擬機器,所以使用者模式核心不能訪問主機系統上的硬體裝置。因此,UML並不適合於除錯那些處理實際硬體的驅動程式。不過,如果所編寫的核心程式不是硬體驅動,例如Linux檔案系統、協議棧等情況,使用UML作為除錯工具還是一個不錯的選擇。

為了方便除錯和測試程式碼,核心提供了許多與核心除錯相關的配置選項。這些選項大部分都在核心配置編輯器的核心開發(kernel hacking)選單項中。在核心配置目錄樹選單的其他地方也還有一些可配置的除錯選項,下面將對他們作一定的介紹。

Page alloc debugging :CONFIG_DEBUG_PAGEALLOC:

不使用該選項時,釋放的記憶體頁將從核心地址空間中移出。使用該選項後,核心推遲移出記憶體頁的過程,因此能夠發現記憶體洩漏的錯誤。

Debug memory allocations :CONFIG_DEBUG_SLAB:

該開啟該選項時,在核心執行記憶體分配之前將執行多種型別檢查,通過這些型別檢查可以發現諸如核心過量分配或者未初始化等錯誤。核心將會在每次分配記憶體前後時設定一些警戒值,如果這些值發生了變化那麼核心就會知道記憶體已經被操作過並給出明確的提示,從而使各種隱晦的錯誤變得容易被跟蹤。

Spinlock debugging :CONFIG_DEBUG_SPINLOCK:

開啟此選項時,核心將能夠發現spinlock未初始化及各種其他的錯誤,能用於排除一些死鎖引起的錯誤。

Sleep-inside-spinlock checking:CONFIG_DEBUG_SPINLOCK_SLEEP:

開啟該選項時,當spinlock的持有者要睡眠時會執行相應的檢查。實際上即使呼叫者目前沒有睡眠,而只是存在睡眠的可能性時也會給出提示。

Compile the kernel with debug info :CONFIG_DEBUG_INFO:

開啟該選項時,編譯出的核心將會包含全部的除錯資訊,使用gdb時需要這些除錯資訊。

Stack utilization instrumentation :CONFIG_DEBUG_STACK_USAGE:

該選項用於跟蹤核心棧的溢位錯誤,一個核心棧溢位錯誤的明顯的現象是產生oops錯誤卻沒有列出系統的呼叫棧資訊。該選項將使核心進行棧溢位檢查,並使核心進行棧使用的統計。

Driver Core verbose debug messages:CONFIG_DEBUG_DRIVER:

該選項位於"Device drivers-> Generic Driver Options"下,開啟該選項使得核心驅動核心產生大量的除錯資訊,並將他們記錄到系統日誌中。

Verbose SCSI error reporting (kernel size =12K) :CONFIG_SCSI_CONSTANTS:

該選項位於"Device drivers/SCSI device support"下。當SCSI裝置出錯時核心將給出詳細的出錯資訊。

Event debugging:CONFIG_INPUT_EVBUG:

開啟該選項時,會將輸入子系統的錯誤及所有事件都輸出到系統日誌中。該選項在產生了詳細的輸入報告的同時,也會導致一定的安全問題。

以上核心編譯選項需要讀者根據自己所進行的核心程式設計的實際情況,靈活選取。在使用以上介紹的三種原始碼級的核心除錯工具時,一般需要選取CONFIG_DEBUG_INFO選項,以使編譯的核心包含除錯資訊。

6. 總結

上面介紹了一些除錯Linux核心的方法,特別是詳細介紹了三種原始碼級的核心除錯工具,以及搭建這些核心除錯環境的方法,讀者可以根據自己的情況從中作出選擇。

除錯工具(例如gdb)的執行都需要作業系統的支援,而此時核心由於一些錯誤的程式碼而不能正確執行對系統的管理功能,所以對核心的除錯必須採取一些特殊的方法進行。以上介紹的三種原始碼級的除錯方法,可以歸納為以下兩種策略:

I、為核心增加除錯Stub,利用除錯Stub進行遠端除錯,這種除錯策略需要target及development機器才能完成除錯任務。

II、將虛擬機器技術與除錯工具相結合,使Linux核心在虛擬機器中執行從而利用偵錯程式對核心進行除錯。這種策略需要製作適合在虛擬機器中執行的系統核心。

由不同的除錯策略決定了進行除錯時不同的工作原理,同時也形成了各種除錯方法不同的軟硬體需求和各自的特點。

另外,需要說明的是核心除錯能力的掌握很大程度上取決於經驗和對整個作業系統的深入理解。對系統核心的全面深入的理解,將能在很大程度上加快對Linux系統核心的開發和除錯。

對系統核心的除錯技術和方法絕不止上面介紹所涉及的內容,這裡只是介紹了一些經常看到和聽到方法。在Linux核心向前發展的同時,核心的除錯技術也在不斷的進步。希望以上介紹的一些方法能對讀者開發和學習Linux有所幫助。

參考資料

參考文獻

[1]Robert Love Linux kernel development機械工業出版社

[2]陳渝 原始碼開發的嵌入式系統軟體分析與實踐 北京航空航天大學出版社

[3]Alessandro Rubini Linux device driver 2se Edition O'Reilly

[4]Jonathan Corbet Linux device driver 3rd Edition O'Reilly

[5]李善平 Linux核心原始碼分析大全 機械工業出版社


作者簡介

相關推薦

Linux 系統核心除錯

教您如何搭建Linux核心除錯環境 /*---------------------------------------------------------------------------------------------------------------------

Linux 系統核心除錯收藏 Linux 系統核心除錯

除錯是軟體開發過程中一個必不可少的環節,在 Linux 核心開發的過程中也不可避免地會面對如何除錯核心的問題。但是,Linux 系統的開發者出於保證核心程式碼正確性的考慮,不願意在 Linux 核心原始碼樹中加入一個偵錯程式。他們認為核心中的偵錯程式會誤導開發者,從而引入不良的修正[1].所以對 Linux

Ubuntu 16.04 Linux系統核心升級

一 檢視系統及核心版本 檢視釋出版本號 cat /etc/issue lsb_release -a 檢視核心版本號 uname -sr uname -a 二 升級核心 Ubuntu核心下載網站:http://kernel.ubuntu.com/~kernel-pp

linux——系統核心引數優化

vim /etc/sysctl.conf net.ipv4.tcp_syncookies = 1 fs.file-max = 999999 net.ipv4.tcp_max_tw_buckets = 6000 net.ipv4.tcp_tw_reuse = 1 net.i

linux 系統核心引數優化 /etc/sysctrl.conf

#禁用包過濾功能 net.ipv4.ip_forward = 0 #禁用所有IP源路由 net.ipv4.conf.default.accept_source_route = 0 #開啟SYN Cookies,當出現SYN等待佇列溢位時,啟用cookies來處理 net.ipv4.tcp_syn

如何編譯和更換Linux系統核心

step0 — 完成編譯和安裝Linux需要的基本條件 核心原始碼 從即可下載程式碼,注意你需要下載的是完整的原始碼包(點選tarball即可下載),而不是patch或change log之類。 編譯需要的軟體 編譯kernel需要一些基本的develop工具軟體

Linux系統核心核心模組那些事

在開機的過程中,是否能夠成功的驅動主機的硬體裝置,是核心的工作,而核心一般都是壓縮檔案,因此在使用核心之前,就需要將核心檔案解壓縮,才能載入主儲存器中。 目前的核心均具有模組化功能 核心與核心模組的位置: 核心:/boot/vmlinuz 或

linux系統核心優化

net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid =

Ubuntu 16.04 Linux系統核心升級方法

Ubuntu 16.04 Linux系統核心升級方法 一、檢視系統及核心版本命令 #檢視釋出版本號 cat /etc/issue lsb_release -a #檢視核心版本號 uname -sr uname -a 二、升級核心方法 #到 Ubuntu網站http:/

更新linux系統核心後分辨率問題解決

今天更新了linux系統核心(升級到4.9版本),方法如下: 下載 linux-headers-4.9.0-040900_4.9.0-040900.201612111631_all.deb linux-headers-4.9.0-040900-gen

Linux系統核心引數優化

Linux伺服器核心引數優化cat >> /etc/sysctl.conf<< EOF# kernel optimizationnet.ipv4.tcp_fin_timeout = 2net.ipv4.tcp_tw_reuse = 1net.ipv4.

Linux系統除錯 C語言程式(使用gdb)

問題描述:Linux 系統下用C語言寫 的指令碼程式,程式執行時出現未知錯誤需要除錯找出問題所在地。(阿里雲Ubuntu系統的伺服器)。 解決方法:使用 gdb工具在終端視窗中除錯 C檔案。 具體方法: (1)安裝 gdb  sudo apt-get install

Linux系統核心正式進入5.0版本時代

知名Linux核心開發人員兼維護人員Greg Kroah-Hartman今天宣佈,Linux Kernel

Linux系統中使用SystemTap除錯核心

SystemTap 是一種新穎的 Linux 核心診斷工具,提供了一種從執行中的 Linux 核心快速和安全地獲取資訊的能力。SystemTap 是核心研發人員和系統管理員的福音,因為這使得他們能夠通過編寫或重用簡單的指令碼來收集核心的實時資料,而無需再忍受修改原始碼、編譯核

一分鐘掌握Linux系統grub.conf配置核心知識

Linux grub grub.con 老男孩IT教育 引導加載程序(Boot loader)是在計算機在加載操作系統內核之前運行的一段小程序。通過這段小程序,可以初始化硬件設備、建立內存空間的映射圖,從而將系統的軟硬件環境加載到一個適合的狀態,以便為最終調用操作系統內核做好準備。通常,引導

linux系統下如何在vscode中除錯C++程式碼

本篇部落格以一個簡單的hello world程式,介紹在vscode中除錯C++程式碼的配置過程。 1. 安裝編譯器 vscode是一個輕量的程式碼編輯器,並不具備程式碼編譯功能,程式碼編譯需要交給編譯器完成。linux下最常用的編譯器是gcc,通過如下命令安裝: sudo apt-get instal

linux核心資料結構以及核心除錯

一、可移植性 1.1 資料型別可移植性 由於核心可能執行在不同的架構上,不同的架構具有不同的機器字長,因而可移植性對核心程式設計非常重要。核心資料使用的資料型別分為 3 個主要型別 標準C型別 明確大小的型別 用作特定核心物件的型別 1.1.1 標準 C 型別 使用標準

Linux系統檢視系統核心與版本號

1、檢視核心版本        1) cat /proc/version        2) uname -a    如上圖所示,核心版本是4.4.114 ,其中x86_64,

[轉] Linux 檢視系統核心版本和發行版本

一、檢視 Linux 核心版本命令 以下兩條命令適合所有 Linux 系統。 1、cat /proc/version 複製 1 2 3 4 5 $ cat /proc/version Linux ve

嵌入式Linux開發——(十六)Linux核心除錯技術

1、核心列印函式printk     ①printk函式與printf函式用法格式完全相同     ②它所列印的字串頭部可以加入“<n>”樣式字元,n=0---7表示這條資訊的記錄  級別     ③對於p