通過(guò)GDB和QEMU調(diào)試Linux內(nèi)核已經(jīng)有很多介紹了,但基本都是制作簡(jiǎn)單的根文件系統(tǒng)。有時(shí)候需要調(diào)試的模塊或者場(chǎng)景需要用到發(fā)行版的Linux,因此本文介紹調(diào)試CentOS內(nèi)核的步驟。
GDB調(diào)試Linux內(nèi)核關(guān)鍵步驟

- 首先需要有一個(gè)可以啟動(dòng)運(yùn)行Cent OS 7的虛擬機(jī)
- 準(zhǔn)備一個(gè)qcow2格式的磁盤(pán)鏡像文件centos.img.qcow2,通過(guò)CentOS的光盤(pán)iso將系統(tǒng)安裝到這個(gè)磁盤(pán)鏡像中。
- 虛擬機(jī)需要連接外部網(wǎng)絡(luò)(例如mount nfs文件系統(tǒng)、調(diào)試內(nèi)核的網(wǎng)絡(luò)代碼等),所以我們要為QEMU虛擬機(jī)選擇合適的網(wǎng)絡(luò)方案
- 通過(guò)將tap和物理網(wǎng)卡加入到bridge
- GDB的調(diào)試需要源代碼和編譯生成的原始文件vmlinux,因此我們要下載Linux內(nèi)核源代碼,完成patch、configure和編譯等工作;
- 編譯Linux內(nèi)核的最終產(chǎn)品為bzImage,因此要能夠更新虛擬機(jī)中的內(nèi)核bzImage以及GRUB配置;
- QEMU內(nèi)置一個(gè)gdbserver,通過(guò)”-s -S”選項(xiàng)來(lái)開(kāi)啟并等待GDB的連接,默認(rèn)端口為1234
- -S freeze CPU at startup (use 'c' to start execution)
- -s shorthand for -gdb tcp::1234
- 也可以在QEMU命令行中通過(guò)gdbserver tcp::1234來(lái)指定gdbserver的端口號(hào)
安裝CentOS 7.1到虛擬磁盤(pán)
從CentOS網(wǎng)站上下載CentOS 7的安裝包CentOS-7-x86_64-DVD-1503-01.iso
qemu-img create centos.img.qcow2 -f qcow2 40G
qemu-system-x86_64 -cdrom CentOS-7-x86_64-DVD-1503-01.iso -hda centos.img.qcow2 -boot d -net nic -net user -m 4096 -vnc 0.0.0.0:1 -enable-kvm -smp 2
- 通過(guò)VNC View連接ip:5901進(jìn)入虛擬機(jī)的控制臺(tái),在控制臺(tái)中完成CentOS 7.1的安裝過(guò)程
- 安裝完成后得到虛擬機(jī)的磁盤(pán)鏡像centos.img.qcow2
- 重新啟動(dòng)QEMU虛擬機(jī):
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2
通過(guò)VNC Viewer連接到虛擬機(jī)的控制臺(tái),確認(rèn)CentOS 7正常啟動(dòng)

QEMU虛擬機(jī)網(wǎng)絡(luò)配置
QEMU支持多種方式的網(wǎng)絡(luò)配置,對(duì)于需要訪問(wèn)外部網(wǎng)絡(luò)的虛擬機(jī),最合適的方式是tap橋接模式,即將虛擬機(jī)網(wǎng)卡對(duì)應(yīng)的tap網(wǎng)卡和Host的物理網(wǎng)卡加入到同一個(gè)bridge中
- 創(chuàng)建bridge并將物理網(wǎng)卡加入到該bridge中
/home/gj/virt/qemu-kernel/net_config.sh
#!/bin/sh
ifconfig enp2s0 0.0.0.0
brctl addbr br0
brctl addif br0 enp2s0
dhclient br0
- QEMU創(chuàng)建虛擬機(jī)時(shí)指定-net nic -net tap,script=/etc/qemu-ifup1,其中qemu-ifup1由QEMU初始化虛擬機(jī)網(wǎng)卡時(shí)調(diào)用,它負(fù)責(zé)將tap網(wǎng)卡加入到bridge中
/etc/qemu-ifup1
#! /bin/sh
# TAP interface will be passed in $1
ifconfig $1 up
brctl addif br0 $1
虛擬機(jī)啟動(dòng)之后可以看到其已經(jīng)通過(guò)DHCP的方式自動(dòng)獲取到ip
編譯Linux內(nèi)核源代碼
下載Linux內(nèi)核的源代碼可以在Linux內(nèi)核網(wǎng)站上下載“干凈”的版本,但是需要自己來(lái)配置內(nèi)核,更簡(jiǎn)單的辦法是在CentOS網(wǎng)站上下載對(duì)應(yīng)版本的rpm源碼包,這樣不需要關(guān)心patch和內(nèi)核配置,比如CentOS 7.1對(duì)應(yīng)的源碼包kernel-3.10.0-229.1.2.el7.src.rpm
- 安裝源碼包
rpm –ivh kernel-3.10.0-229.1.2.el7.src.rpm
- 通過(guò)rpmbuild -bp完成源碼的patch和config工作(-bp表示prepare)
rpmbuild -bp ~/rpmbuild/SPECS/kernel.spec
完成之后我們就得到了一套準(zhǔn)備好的待編譯的源代碼,此時(shí)完全可以將這一套源代碼打包拷貝到別處重復(fù)使用。rpmbuild -bb可以完成代碼的prepare和編譯打包的全部工作,直接生成新內(nèi)核的rpm安裝包。
- 進(jìn)入Linux內(nèi)核源碼目錄
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64/
make bzImage && make modules
生成gdb需要的vmlinux和待安裝的新內(nèi)核bzImage(arch/x86_64/boot/bzImage)
安裝新內(nèi)核
編譯完成需要將生成的內(nèi)核(bzImage)和modules安裝到虛擬機(jī)中去,一種方式是制作rpm包然后拷貝到虛擬機(jī)中去安裝:
make binrpm-pkg INSTALL_MOD_STRIP=1
另外一種方式:在host中將內(nèi)核源碼通過(guò)nfs共享給虛擬機(jī),由虛擬機(jī)自己來(lái)完成install的工作
- 進(jìn)入源碼目錄,修改代碼后編譯
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64
make bzImage
make modules (第一次編譯安裝新內(nèi)核時(shí)需要)
- QEMU啟動(dòng)虛擬機(jī)
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2 -net nic -net tap,script=/etc/qemu-ifup1 -enable-kvm -smp 2
*注意此時(shí)不需要’-s -S’選項(xiàng),同時(shí)可以新增’-enable-kvm’和’-smp 2’來(lái)提高虛擬機(jī)的性能;
- 在虛擬機(jī)中mount Host導(dǎo)出的源碼目錄
showmount -e 192.168.10.115
Export list for 192.168.10.115:
/root/rpmbuild/BUILD 192.168.10.0/24
mount -t nfs 192.168.10.115:/root/rpmbuild/BUILD /mnt/nfs/
- 進(jìn)入源碼目錄安裝新編譯的內(nèi)核
make modules_install INSTALL_MOD_STRIP=1 (第一次編譯安裝新內(nèi)核時(shí)需要)
make install
安裝成功后重啟虛擬機(jī)后通過(guò)uname -a確認(rèn)內(nèi)核更新成功
開(kāi)始調(diào)試內(nèi)核
- 檢查網(wǎng)絡(luò)配置
- 在會(huì)話1中通過(guò)QEMU創(chuàng)建虛擬機(jī)
cd /home/gj/virt/qemu-kernel
qemu-system-x86_64 -m 4096 -vnc 0.0.0.0:1 centos.img.qcow2 -net nic -net tap,script=/etc/qemu-ifup1 -s -S
使用VNC viewer連接ip:5901
- 會(huì)話2中進(jìn)入源碼目錄并gdb
cd ~/rpmbuild/BUILD/kernel-3.10.0-229.1.2.el7/linux-3.10.0-229.1.2.el7.centos.x86_64
gdb vmlinux
- 在gdb中連接QEMU的gdbserver并設(shè)置斷點(diǎn)
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x0000000000000000 in irq_stack_union ()
(gdb) break yfs_mount
Breakpoint 1 at 0xffffffff812b259f: file fs/yfs/super.c, line 777.
(gdb) c
Continuing.
此時(shí)通過(guò)VNC Viewer連接到虛擬機(jī)的控制臺(tái),應(yīng)該可以看到啟動(dòng)的輸出
- 在虛擬機(jī)中執(zhí)行調(diào)試代碼的操作,然后會(huì)話2中會(huì)被break住,此時(shí)像普通程序一樣進(jìn)行g(shù)db調(diào)試;
附一:gdb編譯
GDB在調(diào)試64位Linux內(nèi)核時(shí)會(huì)出錯(cuò):
Remote ‘g’ packet reply is too long
解決辦法是下載GDB的源代碼,如下所示修改remote.c
if (buf_len > 2 * rsa->sizeof_g_packet)
error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
修改為:
if (buf_len > 2 * rsa->sizeof_g_packet) {
//error (_("Remote 'g' packet reply is too long: %s"), rs->buf);
rsa->sizeof_g_packet = buf_len ;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++) {
if (rsa->regs->pnum == -1)
continue;
if (rsa->regs->offset >= rsa->sizeof_g_packet)
rsa->regs->in_g_packet = 0;
else
rsa->regs->in_g_packet = 1;
}
}
重新編譯并安裝GDB
./configure
make && make install
附二:qemu編譯
Redhat/CentOS不推薦直接使用qemu-kvm,而是需要通過(guò)libvirt的接口來(lái)創(chuàng)建虛擬機(jī),因此要使用QEMU推薦下載QEMU源代碼編譯安裝,編譯安裝也很簡(jiǎn)單:
./configure --target-list=x86_64-softmmu
make && make install
附三:虛擬機(jī)組網(wǎng)
橋接
上文介紹中QEMU虛擬機(jī)是通過(guò)橋接方式連接外部網(wǎng)絡(luò),其將虛擬機(jī)對(duì)應(yīng)的tap網(wǎng)卡和物理網(wǎng)卡加入到同一個(gè)bridge中,將物理網(wǎng)卡的ip分配到bridge上,這樣從邏輯上看虛擬機(jī)的報(bào)文都是通過(guò)bridge進(jìn)出

橋接方式的好處是每個(gè)VM都可以獲取到物理機(jī)同網(wǎng)段的ip,因此也可以訪問(wèn)物理機(jī)所屬的網(wǎng)絡(luò)
NAT
有時(shí)虛擬機(jī)不需要訪問(wèn)物理機(jī)所屬的網(wǎng)絡(luò)(比如受ip沖突影響),虛擬機(jī)只需要能夠互相訪問(wèn)同時(shí)可以訪問(wèn)外網(wǎng),這時(shí)NAT方式是比較適合的。其將所有VM對(duì)應(yīng)的tap網(wǎng)卡加入到同一個(gè)bridge中,這個(gè)bridge作為這個(gè)私有網(wǎng)段的網(wǎng)關(guān),每個(gè)VM啟動(dòng)后配置一個(gè)同網(wǎng)段的ip;然后物理網(wǎng)卡為這個(gè)bridge做NAT。

配置方式如下:
- Host網(wǎng)絡(luò)初始化腳本
#!/bin/sh
# create bridge
brctl addbr br0-qemu
ifconfig br0-qemu 192.168.125.1
iptables -t nat -A POSTROUTING -o enp2s0 -j MASQUERADE
iptables -A FORWARD -i enp2s0 -o br0-qemu -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i br0-qemu -o enp2s0 -j ACCEPT
echo 1 > /proc/sys/net/ipv4/ip_forward
- qemu初始化腳本 /etc/qemu-ifup-nat
#! /bin/sh
# TAP interface will be passed in $1
ifconfig $1 promisc up
brctl addif br0 $1
上述方式中所有VM的ip, gateway和DNS都需要配置成固定的。如果要實(shí)現(xiàn)VM自動(dòng)分配私有ip那么還需要在host上安裝一個(gè)本地的DHCP Server