1:前言:
最近幾天學(xué)習(xí)Linux-2.6平臺上的設(shè)備驅(qū)動,所以要建立內(nèi)核及內(nèi)核模塊的調(diào)試平臺.雖然網(wǎng)上有很多相關(guān)教程,但多是基于2.6.26以前的通過補丁安裝的,過程非常復(fù)雜,而且問題比較多.linux從 2.6.26開始已經(jīng)集成了kgdb,只需要重新編譯2.6.26(或更高)內(nèi)核即可.kgdb安裝及模塊調(diào)試過程也遇到不少問題,網(wǎng)上網(wǎng)下不斷的搜索與探索,才算調(diào)通.現(xiàn)在記錄下來,供朋友們參考.
首先說一下,開始本打算安裝kdb進(jìn)行內(nèi)核調(diào)試,后來聽說kdb只能進(jìn)行匯編級別的調(diào)試,所以放棄,改用kgdb.
2: 系統(tǒng)環(huán)境:
虛擬環(huán)境: VMWare Workstation 5.5(英文版)
操作系統(tǒng): CentOS-4.6-i386(原內(nèi)核2.6.9,將會把內(nèi)核升級至2.6.26)
注:CentOS 是RedHat的一個社區(qū)版本.
(由于我們采用的linux kernel 2.6.26已經(jīng)集成kgdb,kgdb再不需要單獨下載)
3:系統(tǒng)的安裝:
在VMWare中新建一臺計算機:
點擊 Next
選中Custom 點擊 Next
選中 New-Workstation 5,點擊Next
選中Linux ,Version選中Other Linux 2.6.x kernel 點擊Next
Virtual machine name 輸入Client(Development) 點擊Next
Processors 選中 One, 點擊Next
Memory 輸入256,點擊Next
Network connection 選中Use network address translation(NAT) (選第一個貌似也可以) 點擊Next
I/O adapter types
SCSI Adapters 選中默認(rèn)的LSI Logic(這里如果你后面使用了SCSI格式的Disk,編譯內(nèi)核時需要添加相應(yīng)的驅(qū)動,我選擇的是IDE的硬盤,kernel默認(rèn)就支持了)
Disk 選中Create a new virtual disk 點擊Next
Virtual Disk Type 選中IDE,點擊Next
Disk capacity Disk size 輸入80G (下面的Allocate all disk space now不要選中,表示在真正使用才分配磁盤空間, Split disk into 2 GB files項,可不選,如果你的系統(tǒng)分區(qū)為fat32格式,最好選中) 點擊Next.
Disk file ,輸入Disk的名稱,如:disk1.vmdk ,點擊Finish.完成
安裝CentOS 4.6(過程略過)
安裝完成后,關(guān)閉計算機,然后Clone一臺同樣的計算機.步驟如下:
點擊VM->Clone
選中默認(rèn)的From current state,點擊Next
選中Create a full clone, 點擊Next
Virtual Machine name 輸入Server(Targe),將克隆的機器命令為目標(biāo)機.
說明一下,kgdb 需要兩臺計算機通過串口進(jìn)行遠(yuǎn)程調(diào)試,兩臺計算機分別為:
Client(Development):開發(fā)機,也稱客戶機,將在該計算機上進(jìn)行程序的開發(fā),GDB將在本計算機上運行.用于輸入命令控制Server(target)的運行.
Server(Target): 目標(biāo)機,也稱服務(wù)器,就是被調(diào)試的計算機,在Development機上開發(fā)好的內(nèi)核模塊程序?qū)⒖截惖絋arget上運行,其運行受到Development命令的控制.
分別為兩個系統(tǒng)增加一個串口,以"Output to named pipe"方式,其中:
Client端選擇"this end is the client", "the other end is a virtual machine"
Server端選擇"this end is the server", "the other end is a virtual machine"
備注: 兩個pipe的名稱要相同,并且選中下面的Connect at power on,及Advanced里面的Yield CPU on poll
以后的部分,Server上的操作與Client上的操作將以不同的背景色顯示,輸入的命令將以不同的字體顏色并帶下劃線顯示.請注意:
Server(Target) 輸入: cat /dev/ttyS0
系統(tǒng)輸出的信息: hello Client(Development) 輸入: echo "hello" >/dev/ttuS0
串口添加完成后,使用如果命令測試:
在Server上cat /dev/ttyS0
然后到Client上 echo "hello" > /dev/ttyS0
這時回到Server上,如果能看到輸入的hello,說明串口通訊正常.
4:升級內(nèi)核2.6.26(添加關(guān)于KGDB的選項)
說明一下,這里是要升級Server的內(nèi)核,因為kgdb是要Server上運行的,但是編譯需要在Client完成(或者你也可以在Server上編譯,之后再拷貝到Client上),因為調(diào)試時Client上的gdb會用到編譯的內(nèi)核及源代碼.Client也需要升級,保證Client同Server上的內(nèi)核一致,這樣Client上編譯的模塊拿到Server上加載就不會有什么問題.
首先下載kernel 2.6.26
我習(xí)慣在windows上下載,然后共享,再到linux使用smbclient連接,拷貝到Linux上.你也可以直接在linux上下載.
smbclient用法 : smbclient //192.168.0.100/share -Uadministrator 回車后,會提示輸入admin的密碼.之后就可以通過get獲取了 192.168.0.100是我在windows主機的IP,share為共享名,-U后面是用戶名
編譯Kernel2.6.26
進(jìn)入Client(Development)系統(tǒng),將linux-2.6.26.tar.bz2拷貝到/usr/src目錄下:
cd /usr/src
tar jxvf linux-2.6.26.tar.bz2
ln -s linux-2.6.26 linux
cd linux
make menuconfig
File System --> 下面把ext3,ext2都編譯進(jìn)內(nèi)核(就是把前面的M變成*)
Kernel Hacking -->
選中Compile the kernel with frame pointers
選中KGDB:kernel debugging with remote gdb
并確認(rèn)以下兩項也是選中的(他們應(yīng)該默認(rèn)是選中的)
> kernel debugging
> Compile the kernel with debug info
對于其它選項,請按實際情況,或你的要求定制.
在其它網(wǎng)友的說明里面,會有Serial port number for KGDB等選項,但是我使用的版本未找到這些選項,所以忽略過.
保存退出
make -j10 bzImage
-j10表示使用10個線程進(jìn)行編譯.
make modules
編譯內(nèi)核模塊
make modules_install
安裝內(nèi)核模塊
make install
安裝內(nèi)核
將Client系統(tǒng)中的linux-2.6.26整個目錄同步到Server上.
在Client系統(tǒng)上運行下列命令:
cd /usr/src/linux
scp -r linux-2.6.26 root@Server(Target)IP:/usr/src/
系統(tǒng)會提示輸入root的密碼,輸入完了就會開始復(fù)制文件了,(這里要注意,如果原系統(tǒng)已經(jīng)是2.6.26的內(nèi)核,可以只拷貝arch/i386/boot/bzImage,及System.map文件到Server上,然后修改/boot/grub/grub.conf,但由于我是從2.6.9升級上來,所以需要將整個linux原代碼目錄拷貝到Server上進(jìn)行升級)
升級Srever系統(tǒng)的內(nèi)核
進(jìn)入Server(Target)系統(tǒng),usr/src目錄:
ln -s linux-2.6.26 linux
cd linux
make modules_install
安裝內(nèi)核模塊
make install
安裝內(nèi)核
安裝完成后,在/boot/目錄下會有即個新添加的文件./boot/grub/grub.conf文件也會添加一個新的啟動項,我的如下(行號不是文件的一部分):
1 # grub.conf generated by anaconda
2 #
3 # Note that you do not have to rerun grub after making changes to this file
4 # NOTICE: You have a /boot partition. This means that
5 # all kernel and initrd paths are relative to /boot/, eg.
6 # root (hd0,0)
7 # kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00
8 # initrd /initrd-version.img
9 #boot=/dev/hda
10 default=0
11 timeout=5
12 splashimage=(hd0,0)/grub/splash.xpm.gz
13 hiddenmenu
14 title CentOS (2.6.26)
15 root (hd0,0)
16 kernel /vmlinuz-2.6.26 ro root=/dev/VolGroup00/LogVol00
17 initrd /initrd-2.6.26.img
18 title CentOS-4 i386 (2.6.9-67.ELsmp)
19 root (hd0,0)
20 kernel /vmlinuz-2.6.9-67.ELsmp ro root=/dev/VolGroup00/LogVol00
21 initrd /initrd-2.6.9-67.ELsmp.img
注意里面的NOTICE說明,我的系統(tǒng)/boot是一個獨立的分區(qū),所以下面配置的文件路徑都是相對于/boot/目錄的,像 /vmlinuz-2.6.26,實際到它的絕對位置應(yīng)該是/boot/vmlinuz-2.6.26. 在你的系統(tǒng)上請根據(jù)實際情況處理.
修改一下grub.conf,修改成下面這樣:
1 # grub.conf generated by anaconda
2 #
3 # Note that you do not have to rerun grub after making changes to this file
4 # NOTICE: You have a /boot partition. This means that
5 # all kernel and initrd paths are relative to /boot/, eg.
6 # root (hd0,0)
7 # kernel /vmlinuz-version ro root=/dev/VolGroup00/LogVol00
8 # initrd /initrd-version.img
9 #boot=/dev/hda
10 default=0
11 timeout=5
12 splashimage=(hd0,0)/grub/splash.xpm.gz
13 hiddenmenu
14 title CentOS (2.6.26)
15 root (hd0,0)
16 kernel /vmlinuz-2.6.26 ro root=/dev/VolGroup00/LogVol00 kgdboc=ttyS0,115200
17 initrd /initrd-2.6.26.img
18 title CentOS (2.6.26) Wait...(kernel debug)
19 root (hd0,0)
20 kernel /vmlinuz-2.6.26 ro root=/dev/VolGroup00/LogVol00 kgdboc=ttyS0,115200 kgdbwait
21 initrd /initrd-2.6.26.img
22 title CentOS-4 i386 (2.6.9-67.ELsmp)
23 root (hd0,0)
24 kernel /vmlinuz-2.6.9-67.ELsmp ro root=/dev/VolGroup00/LogVol00
25 initrd /initrd-2.6.9-67.ELsmp.img
說明:
第一個啟動項在原來的基礎(chǔ)上添加了kgdb的參數(shù)kgdboc=ttyS0,115200
kgdboc 的意思是 kgdb over console,這里將kgdb連接的console設(shè)置為ttyS0,波特率為115200,如果不在內(nèi)核啟動項中配置該參數(shù),可以在進(jìn)入系統(tǒng)后執(zhí)行命令:
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
第二個啟動項,同第一個使用同一個內(nèi)核,只是添加了kgdbwait參數(shù)
kgdbwait 使 kernel 在啟動過程中等待 gdb 的連接。
我的啟動菜單如下:
CentOS(2.6.26)
CentOS(2.6.26)Wait...(kernel debug)
CentOS-4 i386(2.6.9-67.ELsmp)
調(diào)用內(nèi)核模塊,就選擇第一個,如果要調(diào)試內(nèi)核啟動過程,選擇第二個.
5.內(nèi)核調(diào)試
重啟Server,通過啟動菜單第二項CentOS(2.6.26)Wait...(kernel debug)進(jìn)入系統(tǒng). 只到系統(tǒng)出現(xiàn):
kgdb: Registered I/O driver kgdboc
kgdb: Waiting for connection from remote gdb
進(jìn)入Client系統(tǒng).
cd /usr/src/linux
gdb vmlinux
啟動gdb開始準(zhǔn)備調(diào)試:輸出大致如下:
GNU gdb Red Hat Linux (6.3.0.0-1.153.el4rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db
library "/lib/tls/libthread_db.so.1" (gdb) set remotebaud 115200
(gdb) target remote /dev/ttyS0
注意:有的文章講:因為vmware的named piped不能被gdb直接使用,需要使用 socat -d -d /tmp/com_1 /dev/ttyS0轉(zhuǎn)換,然后使用轉(zhuǎn)換后的設(shè)備. socat需要自己下載安裝. 但在我的環(huán)境中,直接使用并沒有出錯.
執(zhí)行該命令后輸出如下:
Remote debugging using /dev/ttyS0
kgdb_breakpoint () at kernel/kgdb.c:1674
1674 wmb(); /*Sync point after breakpoint */
warning: shared library handler failed to enable breakpoint看到上面的內(nèi)容說明已經(jīng)連接成功,但Server上依然是假死狀態(tài),這時你可以像使用本地gdb一樣設(shè)置斷點(break),單步執(zhí)行(step),或其它命令.
(gdb) cont
繼續(xù)執(zhí)行,Server就繼續(xù)下面的系統(tǒng)初始化了.
系統(tǒng)啟動完成后的內(nèi)核調(diào)試:
進(jìn)入Server后,執(zhí)行命令
echo g > /proc/sysrq-trigger
系統(tǒng)同樣會中斷,進(jìn)入假死狀態(tài),等待遠(yuǎn)程gdb的連接.KGDB可能會輸出如下信息:
SysRq: GDB 上面的命令(echo g > /proc/sysrq-trigger)可以有一個快捷鍵(ALT-SysRq-G)代替,當(dāng)然前提是你編譯內(nèi)核時需要選中相關(guān)選項,并且需要修改配置文件:/etc/sysctl.conf , 我用了一下,不太好用.因為有的桌面系統(tǒng)中PrintScreen/SysRq鍵是用于截屏的.所以還是直接用命令來的好!
我在~/.bashrc中添加了一句(添加完保存后,要執(zhí)行source ~/.bashrc應(yīng)用該配置):
alias debug='echo g > /proc/sysrq-trigger'
之后就可以直接輸入debug來使內(nèi)核進(jìn)入調(diào)試狀態(tài).
Server進(jìn)入調(diào)試狀態(tài)后,轉(zhuǎn)換到Client系統(tǒng),重復(fù)上面的步驟.
6. Linux內(nèi)核模塊(設(shè)備驅(qū)動)的調(diào)試
編寫內(nèi)核模塊,及Makefile
我使用的例子是Linux Device Driver 3中的例子scull. 你可以從這里下載LDD3的所有例子程序.
進(jìn)入Client系統(tǒng),解壓example.tar.gz,將其中的example/scull目錄拷貝到/root/scull
然后執(zhí)行:
cd /root/scull
make
編譯應(yīng)該會出錯,因為Linux Device Driver 3的例子程序是基于2.6.10內(nèi)核的,請參靠下面的說明修改這些錯誤:
編譯scull驅(qū)動,完成后,scull目錄應(yīng)該多出了scull.ko及其它中間文件.
scp scull.ko root@targetIp:/root/
將scull.ko模塊文件拷貝到Server上.系統(tǒng)應(yīng)該會提示輸入密碼:
root@targetIp's password:輸入正確密碼后,應(yīng)該能看到如下信息,表示復(fù)制成功了:
scull.ko 100% 258k 258.0kb/s 00:00進(jìn)入Server系統(tǒng)輸入:
cd /root/
insmod scull.ko
加載scull模塊.
cat /sys/module/globalmem/sections/.text
顯示scull模塊的.text段地址.運行該命令后,會返回一個16進(jìn)制的地址,如:
0xd099a000echo g > /proc/sysrq-trigger
現(xiàn)在Server系統(tǒng)變成等待狀態(tài).
再次進(jìn)入Client系統(tǒng):
cd /usr/src/linux
gdb vmlinux
(gdb) set remotebaud 115200
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
kgdb_breakpoint () at kernel/kgdb.c:1674
1674 wmb(); /*Sync point after breakpoint */
warning: shared library handler failed to enable breakpoint出現(xiàn)上面的信息表示連接成功,但此時還不可以設(shè)置斷點.我試了N次,現(xiàn)在設(shè)置端點后,無論Server上對scull做什么操作,Client上的gdb都不會停止.也有人說這是gdb的BUG,gdb無法正常解析內(nèi)核模塊的符號信息. 說是下載kgdb網(wǎng)站上的gdbmod可以解決該問題,但我試了之后發(fā)現(xiàn)根本沒有用. 所以只能通過命令手動加載相關(guān)符號信息:
(gdb) add-symbol-file /root/scull/scull.ko 0xd099a000
注意: 0xd099a000地址是在上面獲取的,命令輸入完了,系統(tǒng)會有如下提示信息(第二行后面的y是自己輸入的:
add symbol table from file "/root/scull/scull.ko" at .text_addr = 0xd099a000
(y or n)y
Reading symbols from /root/scull/scull.ko...done. (gdb)break scull_write
Breakpoint 1 at 0xd099a2d9: file /root/scull/main.c,line 338.(gdb)break scull_read
Breakpoint 2 at 0xd099a1a2: file /root/scull/main.c,line 294(gdb)cont
ContinuingClient上的工作暫停,回到Server系統(tǒng)上:
Server現(xiàn)在處于運行狀態(tài),在Server上運行下面的命令:
cat /proc/devices | grep "scull"
查看scull模塊分配的major.我的系統(tǒng)中返回如下:
253 scull
253 scullp
253 scullamknod /dev/scull c 253 0
253是剛才查詢到的版本號.
echo "this is a test " > /dev/scull
測試輸入函數(shù):scull_write. 該命令輸入完成后,進(jìn)程應(yīng)該會停下來, 請切換到Client系統(tǒng).
cat /dev/scull
測試輸出函數(shù):scull_read. 該命令輸入完成后,進(jìn)程應(yīng)該會停下來, 請切換到Client系統(tǒng).
回到Client系統(tǒng),應(yīng)該能看到gdb的輸出信息:
Breakpoint 1, scull_write (filp=0xce5870c0,buf=0xb7f44000
"this is a test\nias | /usr/bin/which --tty-only --read-alias
--show-dot --show-tilde'\n",count=15,f_pos=0xce5c5f9c)
at /root/scull/main.c:338
338 {
(gdb)_ 現(xiàn)在就可以像調(diào)試本地程序一樣調(diào)試scull.ko模塊了.
以同樣的方法也可以調(diào)試其它函數(shù).(初始化函數(shù)暫時沒想到辦法調(diào)試,因為使用這種方法需要先加載后,才可以進(jìn)行調(diào)試)
你可以自由使用和轉(zhuǎn)載本文檔,轉(zhuǎn)載時請注明出處. (jie123108@163.com)
如果可以,我想寫一個腳本自動完成這個添加符號文件的過程,簡化調(diào)試過程.
Linux Device Driver 3rd中的scull 例程在2.6.26上編譯出錯的問題
1。scripts/Makefile.build:46: *** CFLAGS was changed in "examples/scull/Makefile". Fix it to use EXTRA_CFLAGS。 停止。
解決方法:將 Makefile 中的 CFLAGS 改為 EXTRA_CFLAGS
2. examples/scull/main.c:17:26: error: linux/config.h: 沒有該文件或目錄
解決方法: 將 main.c 中的這條 include 語句注釋掉。
3. examples/scull/access.c: 在函數(shù)‘scull_u_open’中: examples/scull/access.c:107: 錯誤: 提領(lǐng)指向不完全類型的指針
解決方法:access.c 中添加:#include <linux/sched.h>
4. examples/scull/access.c: 在函數(shù)‘scull_access_setup’中:
examples/scull/access.c:355: 警告: 格式字符串不是一個字面字符串而且沒有待格式化的實參
解決方法:將 kobject_set_name(&dev->cdev.kobj, devinfo->name); 改為:
kobject_set_name(&dev->cdev.kobj, "%s", devinfo->name);
因為 kobject_set_name 有一個像 printf 一樣的參數(shù)表。
補充 : 老外作的改動http://www.cs.fsu.edu/~baker/devices/lxr/source/2.6.25/ldd-examples/基本上已經(jīng)可以編譯了
摘錄自: <<Linux Device Driver 3 中的代碼在 2.6.27 中編譯不能通過的問題>>
參考資料:
<<Using 2.6.26 Linux Kernel Debugger (KGDB) with VM>>
<<VMware環(huán)境下用kgdb調(diào)試內(nèi)核>>
<<Using 2.6.26 Linux Kernel Debugger (KGDB) with VM>>
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/jie12310/archive/2009/09/18/4564853.aspx