赞
踩
最近几天学习Linux-2.6平台上的设备驱动,所以要建立内核及内核模块的调试平台.虽然网上有很多相关教程,但多是基于2.6.26以前的通过补丁安装的,过程非常复杂,而且问题比较多.linux从 2.6.26开始已经集成了kgdb,只需要重新编译2.6.26(或更高)内核即可.kgdb安装及模块调试过程也遇到不少问题,网上网下不断的搜索与探索,才算调通.现在记录下来,供朋友们参考.
首先说一下,开始本打算安装kdb进行内核调试,后来听说kdb只能进行汇编级别的调试,所以放弃,改用kgdb.
虚拟环境: VMWare Workstation 5.5(英文版)
操作系统: CentOS-4.6-i386(原内核2.6.9,将会把内核升级至2.6.26)
注:CentOS 是RedHat的一个社区版本.
(由于我们采用的linux kernel 2.6.26已经集成kgdb,kgdb再不需要单独下载)
在VMWare中新建一台计算机:
系统输出的信息: hello
说明一下,这里是要升级Server的内核,因为kgdb是要Server上运行的,但是编译需要在Client完成(或者你也可以在Server上编译,之后再拷贝到Client上),因为调试时Client上的gdb会用到编译的内核及源代码.Client也需要升级,保证Client同Server上的内核一致,这样Client上编译的模块拿到Server上加载就不会有什么问题.
首先下载kernel 2.6.26
我习惯在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
将Client系统中的linux-2.6.26整个目录同步到Server上.
升级Srever系统的内核
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 |
修改一下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 |
重启Server,通过启动菜单第二项CentOS(2.6.26)Wait...(kernel debug)进入系统. 只到系统出现:
kgdb: Registered I/O driver kgdboc kgdb: Waiting for connection from remote gdb
进入Client系统.
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"
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看到上面的内容说明已经连接成功,但Server上依然是假死状态,这时你可以像使用本地gdb一样设置断点(break),单步执行(step),或其它命令.
系统启动完成后的内核调试:
进入Server后,执行命令
SysRq: GDB上面的命令(echo g > /proc/sysrq-trigger)可以有一个快捷键(ALT-SysRq-G)代替,当然前提是你编译内核时需要选中相关选项,并且需要修改配置文件:/etc/sysctl.conf , 我用了一下,不太好用.因为有的桌面系统中PrintScreen/SysRq键是用于截屏的.所以还是直接用命令来的好!
编写内核模块,及Makefile
我使用的例子是Linux Device Driver 3中的例子scull. 你可以从这里下载LDD3的所有例子程序.
root@targetIp's password:输入正确密码后,应该能看到如下信息,表示复制成功了:
scull.ko 100% 258k 258.0kb/s 00:00
0xd099a000
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次,现在设置端点后,无论Server上对scull做什么操作,Client上的gdb都不会停止.也有人说这是gdb的BUG,gdb无法正常解析内核模块的符号信息. 说是下载kgdb网站上的gdbmod可以解决该问题,但我试了之后发现根本没有用. 所以只能通过命令手动加载相关符号信息:
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.
Breakpoint 1 at 0xd099a2d9: file /root/scull/main.c,line 338.
Breakpoint 2 at 0xd099a1a2: file /root/scull/main.c,line 294
Continuing
253 scull 253 scullp 253 sculla
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)_
以同样的方法也可以调试其它函数.(初始化函数暂时没想到办法调试,因为使用这种方法需要先加载后,才可以进行调试)
你可以自由使用和转载本文档,转载时请注明出处. (jie123108@163.com)
如果可以,我想写一个脚本自动完成这个添加符号文件的过程,简化调试过程.
1 开启虚拟机,虚拟机运行到 kgdb: Waiting for connection from remote gdb
2. 在Host机上运行: socat tcp-listen:8888 /tmp/vbox2, 其中/tmp/vbox2为管道文件,它是目标机串口的重定向目的文件,socat将这个管道文件又重定向到tcp socket的8888端口。
3. 开启一个新的虚拟终端,cd path/to/kernel/source/tree, 然后执行gdb ./vmlinux
输出
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...
(gdb) set-remote
set remote baud rate to 115200c/s
set remote target to local tcp socket
kgdb_breakpoint () at kernel/kgdb.c:1721
1721 wmb(); /* Sync point after breakpoint */
(gdb) c
Continuing.
目标机会一直启动,直到提示输入用户名密码。
4. 进入目标机,输入用户名密码(推荐使用字符界面下的root用户),输入g命令,目标机被断下,控制移交到Host机中的gdb中。(目标机root的用户目录中的.bashrc中添加一行alias g='echo g>/proc/sysrq-trigger')
5. 在Host机中的gdb中
(gdb) set-mod-break
set breakpoint in system module init function
Breakpoint 1 at 0xc014bac5: file kernel/module.c, line 2288.
(gdb) c
Continuing.
6. 在目标机中
insmod klogger2.ko
目标机再次断下,控制权移交Host机中的gdb
7. 在Host机中的gdb中
[New Thread 4693]
[Switching to Thread 4693]
Breakpoint 1, sys_init_module (umod=0x0, len=0, uargs=0x0)
at kernel/module.c:2288
2288 if (mod->init != NULL)
(gdb) print-mod-segment
Name:.note.gnu.build-id Address:0xdf977058
Name:.text Address:0xdf975000
Name:.rodata Address:0xdf977080
Name:.rodata.str1.4 Address:0xdf9774b4
Name:.rodata.str1.1 Address:0xdf977522
Name:.parainstructions Address:0xdf977a00
Name:.data Address:0xdf978440
Name:.gnu.linkonce.this_module Address:0xdf978480
Name:.bss Address:0xdf978a00
Name:.symtab Address:0xdf977a08
Name:.strtab Address:0xdf978078
(gdb) add-symbol-file /home/done/programs/linux-kernel/vlogger/klogger2.ko 0xdf975000 -s .data 0xdf978440 -s .bss 0xdf978a00
add symbol table from file "/home/done/programs/linux-kernel/vlogger/klogger2.ko" at
.text_addr = 0xdf975000
.data_addr = 0xdf978440
.bss_addr = 0xdf978a00
(y or n) y
Reading symbols from /home/done/programs/linux-kernel/vlogger/klogger2.ko...done.
(gdb) b hook_init
Breakpoint 2 at 0xdf976d19: file /home/done/programs/linux-kernel/vlogger/hook.c, line 255.
(gdb)
你可以调试自己些的LKM模块了
附gdb的初始化配置文件~/.gdbinit
define set-remote
echo set remote baud rate to 115200c/s\n
set remotebaud 115200
echo set remote target to local tcp socket\n
target remote tcp:localhost:8888
end
define set-mod-break
echo set breakpoint in system module init function\n
break kernel/module.c:2288
end
define print-mod-segment
set $sect_num=mod->sect_attrs->nsections
set $cur=0
while $cur < $sect_num
printf "Name:%-s Address:0x%x\n",mod->sect_attrs->attrs[$cur]->name,mod->sect_attrs->attrs[$cur]->address
set $cur=$cur+1
end
end
后记:gdb的调试脚本真难写,简单的字符串变量连接和等价判断都显得十分困难,不知道是我水平太差还是gdb的脚本功能太弱,总之比起Windbg来说,内核调试困难程度上了个等级。
创建时间:2005-09-09
文章属性:原创
文章提交:xcspy (xcspy.com_at_gmail.com)
作者:xcspy成员 ladybug
E-mail:xcspy.com@gmail.com
主页:www.xcspy.com
1. 几种内核调试工具比较
kdb:只能在汇编代码级进行调试;
优点是不需要两台机器进行调试。
gdb:在调试模块时缺少一些至关重要的功能,它可用来查看内核的运行情况,包括反汇编内核函数。
kgdb:能很方便的在源码级对内核进行调试,缺点是kgdb只能进行远程调试,它需要一根串口线及两台机器来调试内核(也可以是在同一台主机上用vmware软件运行两个操作系统来调试)
使用kdb和gdb调试内核的方法相对比较简单,这里只描述如何使用kgdb来调试内核。
2.软硬件准备
环境:
一台开发机developer(192.168.16.5 com1),一台测试机target(192.168.16.30 com2),都预装redhat 9;一根串口线
下载以下软件包:
linux内核2.4.23 linux-2.4.23.tar.bz2
kgdb内核补丁1.9版 linux-2.4.23-kgdb-1.9.patch
可调试内核模块的gdb gdbmod-1.9.bz2
3.ok,开始
3.1 测试串口线
物理连接好串口线后,使用一下命令进行测试,stty可以对串口参数进行设置
在developer上执行:
stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
echo hello > /dev/ttyS0
在target上执行:
stty ispeed 115200 ospeed 115200 -F /dev/ttyS1
cat /dev/ttyS1
串口线没问题的话在target的屏幕上显示hello
3.2 安装与配置
3.2.1 安装
下载linux-2.4.23.tar.bz2,linux-2.4.23-kgdb-1.9.patch,gdbmod-1.9.bz2到developer的/home/liangjian目录
*在developer机器上
#cd /home/liangjian
#bunzip2 linux-2.4.23.tar.bz2
#tar -xvf linux-2.4.23.tar
#bunzip2 gdbmod-1.9.bz2
#cp gdbmod-1.9 /usr/local/bin
#cd linux-2.4.23
#patch -p1 < ../linux-2.4.23-kgdb-1.9.patch
#make menuconfig
在Kernel hacking配置项中将以下三项编译进内核
KGDB: Remote (serial) kernel debugging with gdb
KGDB: Thread analysis
KGDB: Console messages through gdb
注意在编译内核的时候需要加上-g选项
#make dep;make bzImage
使用scp进行将相关文件拷贝到target上(当然也可以使用其它的网络工具)
#scp arch/i386/boot/bzImage root@192.168.16.30:/boot/vmlinuz-2.4.23-kgdb
#scp System.map root@192.168.16.30:/boot/System.map-2.4.23-kgdb
#scp arch/i386/kernel/gdbstart root@192.168.16.30:/sbin
gdbstart为kgdb提供的一个工具,用于激活内核钩子,使内核处于调试状态
3.2.2 配置
*在developer机器上
在内核源码目录下编辑一文件.gdbinit(该文件用以对gdb进行初始化),内容如下:
#vi .gdbinit
define rmt
set remotebaud 115200
target remote /dev/ttyS0
end
#
以上在.gdbinit中定义了一个宏rmt,该宏主要是设置使用的串口号和速率
*在target机器上
编辑/etc/grub.conf文件,加入以下行:
#vi /etc/grub.conf
title Red Hat Linux (2.4.23-kgdb)
root (hd0,0)
kernel /boot/vmlinuz-2.4.23-kgdb ro root=/dev/hda1
#
在root目录下建立一个脚本文件debugkernel,内容如下:
#vi debug
#!/bin/bash
gdbstart -s 115200 -t /dev/ttyS1 <<EOF
EOF
#chmod +x debugkernel
这个脚本主要是调用gdbstart程序设置target机上使用的串口及其速率,并使内核处于调试状态
3.3 开始调试
target上的内核或内核模块处于调试状态时,可以查看其变量、设置断点、查看堆栈等,并且是源码级的调试,和用gdb调试用户程序一样
3.3.1 内核启动后调试
*在target机器上
重启系统,选择以 2.4.23-kgdb内核启动,启动完成后运行debugkenel,
这时内核将停止运行,在控制台屏幕上显示信息,并等待来自developer的
串口连接
#./debug
About to activate GDB stub in the kernel on /dev/ttyS1
Waiting for connection from remote gdb...
*在developer机器上
#cd /home/liangjian/linux-2.4.23
# gdb vmlinux
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 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"...
执行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
这时target上的内核处于调试状态,可以查看其变量、设置断点、查看堆栈等,和用gdb调试用户程序一样
查看堆栈
(gdb) bt
#0 breakpoint () at kgdbstub.c:1005
#1 0xc0387f48 in init_task_union ()
#2 0xc01bc867 in gdb_interrupt (irq=3, dev_id=0x0, regs=0xc0387f98) at
gdbserial.c:158
#3 0xc010937b in handle_IRQ_event (irq=3, regs=0xc0387f98, action=0xce5a9860)
at irq.c:452
#4 0xc0109597 in do_IRQ (regs=
{ebx = -1072671776, ecx = -1, edx = -1070047232, esi = -1070047232, edi
= -1070047232, ebp = -1070039092, eax = 0, xds
= -1070071784, xes = -1070071784, orig_eax = -253, eip = -1072671729, xcs =
16, eflags = 582, esp = -1070039072, xss = -1072671582}) at irq.c:639
#5 0xc010c0e8 in call_do_IRQ ()
查看jiffies变量的值
(gdb) p jiffies
$1 = 76153
如果想让target上的内核继续运行,执行continue命令
(gdb) continue
Continuing.
3.3.2 内核在引导时调试
kgdb可以在内核引导时就对其进行调试,但并不是所有引导过程都是可调试的,如在kgdb 1.9版中,它在init/main.c的start_kernel()函数中插入以下代码:
start_kernel()
{
......
smp_init();
#ifdef CONFIG_KGDB
if (gdb_enter) {
gdb_hook(); /* right at boot time */
}
#endif
......
}
所以在smp_init()之前的初始化引导过程是不能调试的。
另外要想让target的内核在引导时就处于调试状态,需要修改其/etc/grub.conf文件为如下形式:
title Red Hat Linux (2.4.23-kgdb)
root (hd0,0)
kernel /boot/vmlinuz-2.4.23-kgdb ro root=/dev/hda1 gdb gdbttyS=1 gdbbaud=115200
*在target机器上
引导2.4.23-kgdb内核,内核将在短暂的运行后暂停并进入调试状态,打印如下信息:
Waiting for connection from remote gdb...
*在developer机器上
#cd /home/liangjian/linux-2.4.23
# gdb vmlinux
GNU gdb Red Hat Linux (5.3post-0.20021129.18rh)
Copyright 2003 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"...
执行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
查看当前堆栈
(gdb) bt
#0 breakpoint () at kgdbstub.c:1005
#1 0xc0387fe0 in init_task_union ()
#2 0xc01bc984 in gdb_hook () at gdbserial.c:250
#3 0xc0388898 in start_kernel () at init/main.c:443
在do_basic_setup函数处设置断点,并让内核恢复运行
(gdb) b do_basic_setup
Breakpoint 1 at 0xc0388913: file current.h, line 9.
(gdb) continue
Continuing.
[New Thread 1]
[Switching to Thread 1]
Breakpoint 1, do_basic_setup () at current.h:9
9 __asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL));
内核在do_basic_setup断点处停止运行后查看当前堆栈
(gdb) bt
#0 do_basic_setup () at current.h:9
(gdb)
3.3.3 内核模块调试调试
要想调试内核模块,需要相应的gdb支持,kgdb的主页上提供了一个工具gdbmod,它修正了gdb 6.0在解析模块地址时的错误,可以用来正确的调试内核模块
*在developer机器上
写了个测试用的内核模块orig,如下:
void xcspy_func()
{
printk("<1>xcspy_func\n");
printk("<1>aaaaaaaaaaa\n");
}
int xcspy_init()
{
printk("<1>xcspy_init_module\n");
return 0;
}
void xcspy_exit()
{
printk("<1>xcspy_cleanup_module\n");
}
module_init(xcspy_init);
module_exit(xcspy_exit);
编译该模块:
#cd /home/liangjian/lkm
#gcc -D__KERNEL__ -DMODULE -I/home/liangjian/linux-2.4.23/include -O -Wall -g -c -o orig.o orig.c
#scp orig.o root@192.168.16.30:/root
开始调试:
# gdbmod vmlinux
GNU gdb 6.0
Copyright 2003 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 "i686-pc-linux-gnu"...
设置符号文件的搜索路径
(gdb) set solib-search-path /home/liangjian/lkm
执行rmt宏
(gdb) rmt
breakpoint () at kgdbstub.c:1005
1005 atomic_set(&kgdb_setting_breakpoint, 0);
设置断点使得可以调试内核模块的init函数,查内核源码可知,内核是通过module.c文件的第566行(sys_init_module函数中)mod->init来调用模块的init函数的
(gdb) b module.c:566
Breakpoint 1 at 0xc011cd83: file module.c, line 566.
(gdb) c
Continuing.
[New Thread 1352]
[Switching to Thread 1352]
这时在target机器上执行insmod orig.o,developer则相应的在断点处被暂停,如下
Breakpoint 1, sys_init_module (name_user=0xc03401bc "\001",
mod_user=0x80904d8) at module.c:566
566 if (mod->init && (error = mod->init()) != 0) {
使用step命令进入模块的init函数
(gdb) step
xcspy_init () at orig.c:12
12 printk("<1>xcspy_init_module\n");
(gdb) n
15 }
(gdb)
说明:
调试内核模块的非init函数相对比较简单,只要先在target上执行insmod orig.o,这时由于模块的符号被加载,可以直接在developer的gdb中对想调试的模块函数设置断点,如bt xcspy_func,后面当xcspy_func被调用时就进入了调试状态。
如果想调试内核模块的init函数,由于在执行insmod之前模块的符号还没有被加载,不能直接对模块的init函数设置断点,所以相对来说要困难一些。可以采用两种变通的方法:1,采用上面介绍的在内核调用模块的init函数被调用之前的某处插入断点,如bt sys_init_module()或bt module.c:566;2,在developer上让内核处于运行状态,在target上先执行一遍insmod orig.o,这时orig.o的符号已经被加载到内存中,可以直接在developer的gdb中对模块的init函数设置断点,如bt xcspy_init,然后在target上rmmod orig.o,当下次在target上重新加载orig.o时就进入了调试状态,developer在xcspy_init处被暂停。
重启服务器
向sysrq文件中写入1是为了开启SysRq功能。根据linux/Documentations/sysrq.txt中所说:SysRq代表的是Magic System Request Key。开启了这个功能以后,只要内核没有挂掉,它就会响应你要求的任何操作。但是这需要内核支持(CONFIG_MAGIC_SYSRQ选项)。向/proc/sys/kernel/sysrq中写入0是关闭sysrq功能,写入1是开启,其他选项请参考sysrq.txt。需要注意的是,/proc/sys/kernel/sysrq中的值只影响键盘的操作。
那么怎么使用SysRq键呢?
在x86平台上,组合键”<ALT> + SysRq + <command key>“组成SysRq键以完成各种功能。但是,在一些键盘上可能没有SysRq键。SysRq键实际上就是”Print Screen“键。并且可能有些键盘不支持同时按三个按键,所以你可以”按住ALT键“,”按一下SysRq键“,再”按一下<command key>键“,如果你运气好的话,这个会有效果的。不过放心,现在的键盘一般都支持同时按3个或3个以上的键。
<command key>有很多,这里只挑几个来说,其他的可以参考sysrq.txt文件。
从文件名字就可以看出来这两个是有关系的。写入/proc/sysrq-trigger中的字符其实就是sysrq.txt中说的<command key>键所对应的字符,其功能也和上述一样。
所以,这两行命令先开启SysRq功能,然后用'b'命令让计算机立刻重启。
/proc/sysrq-trigger该文件能做些什么事情呢?
# 立即重新启动计算机 (Reboots the kernel without first unmounting file systems or syncing disks attached to the system)
echo "b" > /proc/sysrq-trigger
# 立即关闭计算机(shuts off the system)
echo "o" > /proc/sysrq-trigger
# 导出内存分配的信息 (可以用/var/log/message 查看)(Outputs memory statistics to the console)
echo "m" > /proc/sysrq-trigger
# 导出当前CPU寄存器信息和标志位的信息(Outputs all flags and registers to the console)
echo "p" > /proc/sysrq-trigger
# 导出线程状态信息 (Outputs a list of processes to the console)
echo "t" > /proc/sysrq-trigger
# 故意让系统崩溃 ( Crashes the system without first unmounting file systems or syncing disks attached to the system)
echo "c" > /proc/sysrq-trigger
# 立即重新挂载所有的文件系统 (Attempts to sync disks attached to the system)
echo "s" > /proc/sysrq-trigger
# 立即重新挂载所有的文件系统为只读 (Attempts to unmount and remount all file systems as read-only)
echo "u" > /proc/sysrq-trigger
呵呵,此外还有两个,类似于强制注销的功能
e — Kills all processes except init using SIGTERM
i — Kills all processes except init using SIGKILL
由于很多原因,Linus没有将调试器放在内核中,这也使得Linux内核调试变得比较困难,不过还好有类似KGDB等辅助工具,通过它们,我们也可以完成Linux内核的调试。
一、环境准备
目标机系统:Ubuntu 8.04 i386 desktop 32bit,目标机使用的是虚拟机系统
开发机系统:Ubuntu 8.04 i386 desktop 32bit(开发机系统用什么不是很重要,不一定必须相同)。
1) 目标机环境准备
在目标机上需要做的主要是编译并使用调试版本的内核。
1. 编译内核,2.6.26以上的内核内置KGDB,推荐使用gconfig,并保证Kernel Hacking下的enable KGDB,enable the Magic SysRq key, enable Compile the kernel with debug info选中;
2. 使用调试版本的内核,并设置为启动时等待GDB连接
以上两点可以参考文章:《VirtualBox下Ubuntu8.10的KGDB内核调试》
可以将目标机的启动方式改为字符界面启动,用如下的方法:在/etc/目录下建立文件inittab,并在其中加入下面一行:
id:3:initdefault:
然后将/etc/rc3.d/下的S30gdm改成K30gdm:
sudo mv /etc/rc3.d/S30gdm /etc/rc3.d/K30gdm
由于使用了字符界面启动,因此Magic SysRq key将只能以命令的形式使用,Alt+G+SysRq组合键不再有效,而只能通过echo g>/proc/sysrq-trigger命令。可以在bash中设置一个alias,以免每次都要输入一长串命令。由于上述命令需要root权限,因此推荐修改root用户的bash配置文件.bashrc,在最后加入一行:
alias g=’echo g > /proc/sysrq-trigger’
2) 开发机环境准备
由于目标机使用的是虚拟机,因此开发机需要做一些虚拟机相关的配置尤其是串口设置(可以参考文章:《Ubuntu下VirtualBox虚拟机串口设置》)。其次是gdb调试器的一些设置。
将目标机中编译好的内核树copy到开发机下面(注意符号连接的指向改变),切换到内核树目录下,建立一下三个gdb脚本文件:
set-remote文件(用于gdb连接到目标机的脚本):
echo set remote baud rate to 115200c/s\n
set remotebaud 115200
echo set remote target to local tcp socket\n
target remote tcp:localhost:8888
set-mod-break文件(用于设置模块加载时的断点):
echo set breakpoint in system module init function\n
break kernel/module.c:2288
print-mod-segment文件(用于显示所加载模块在内存中的各个段的地址):
set $sect_num=mod->sect_attrs->nsections
set $cur=0
while $cur < $sect_num
printf "Name:%-s Address:0x%x\n",mod->sect_attrs->attrs[$cur]->name,mod->sect_attrs->attrs[$cur]->address
set $cur=$cur+1
end
三、调试流程
1) 启动虚拟机中的目标机系统,系统在运行到等待gdb连接时停下;
2) 在开发机中使用socat tcp4-listen:8888 /tmp/vbox将主机中的/tmp/vbox管道(与目标机的串口相连)与主机中端口为8888的socket相连;
3) 进入到开发机中的Linux内核树目录下,使用gdb /.vmlinuz进入调试模式;
4) 在gdb中执行source set-remote连接到目标机,并输入c(continue)命令继续启动目标机;
5) 目标机起动后,在目标机中输入g(echo g > /proc/sysr-trigger命令的别名)命令,断下目标机,并将控制权限转交给开发机中的gdb;
6) 进入开发机的gdb,执行命令source set-mod-break,在模块加载处设置断点,并输入c命令继续执行目标机;
7) 在目标机中使用命令insmod xxx.ko加载LKM模块,会自动产生断点,执行再次返回到开发机中的gdb;
8) 在开发机的gdb中执行source print-mod-segment,显示刚加载的模块xxx.ko的各个段在内存中的地址,然后继续在gdb中执行命令add-symbol-file /dev-machine/path/to/xxx.ko 0xe11b7000 –s .data 0xe11b5800 –s .bss 0xe11b8050,0xe11b7000是.text段的地址,并确定加载符号;
9) 这样就可以在开发中的gdb中设置断点了,命令b init_module_func将在模块初始化函数入口点设置断点,设置断点后用c命令执行目标机,接着会在init_module_func处触发断点,然后可以用step,next,print等gdb命令进行单步调试和变量查看了。
QEMU 提供两种模拟模式。第一种,系统模拟,安装完全的虚拟机。运行在该系统的软件看到的计算机与主机系统完全不同 — 例如,可以在实际的 x86-64 openSUSE 计算机上运行 PowerPC Debian 系统。用户模式模拟没有这么完整。这种方式下,QEMU 模拟库会用于每个二进制文件,他们将主计算机看成自己的,因此 PowerPC 二进制文件能看到主机 x86-64 openSUSE 系统的 /etc 目录和其他配置文件。用户模式模拟能简化对本地资源、网络等的访问。
每个模拟方式都有其安装要求。对于系统模拟,安装客户操作系统就像安装一台单独的计算机(下载并使用预先配置的磁盘镜像是一个替代方法)。对于用户空间模拟,不需要安装整个系统,但要考虑安装使用软件所需的支持库。也许还需要配置跨编译器以生成想要测试的二进制文件。
17.3. 使用 qemu-img
qemu-img 命令行工具是 Xen 和 KVM 用来格式化各种文件系统的。可使用 qemu-img 格式化虚拟客户端映像、附加存储设备以及网络存储。qemu-img 选项及用法如下。
格式化并创建新映像或者设备
创建新磁盘映像文件名为 size,格式为 format。
# qemu-img create [-6] [-e] [-b base_image] [-f format] filename [size]
If base_image is specified, then the image will record only the differences from base_image. No size needs to be specified in this case. base_image will never be modified unless you use the "commit" monitor command.
将现有映像转换成另一种格式
转换选项是将可识别格式转换为另一个映像格式。
命令格式:
# qemu-img convert [-c] [-e] [-f format] filename [-O output_format] output_filename
convert the disk image filename to disk image output_filename using format output_format. it can be optionally encrypted ("-e" option) or compressed ("-c" option).
only the format "qcow" supports encryption or compression. the compression is read-only. it means that if a compressed sector is rewritten, then it is rewritten as uncompressed data.
加密法是使用非常安全的 128 位密钥的 AES 格式。使用长密码(16 个字符以上)获得最大程度的保护。
当使用可增大的映像格式,比如 qcow 或 cow 时,映像转换可帮助您获得较小的映像。在目的映像中可检测并压缩空白字段。
获得映像信息
info 参数显示磁盘映像信息。info 选项的格式如下:
# qemu-img info [-f format] filename
给出磁盘映像文件名信息。使用它可获得在磁盘中保留空间大小,它可能与显示的大小有所不同。如果在磁盘映像中保存有 vm 快照,则此时也会显示。
支持格式
映像格式通常是自动获取的。支持以下格式:
raw
Raw 磁盘映像格式(默认)。这个格式的优点是可以简单、容易地导出到其它模拟器中。如果您的文件系统支持中断(例如在 Linux 中的 ext2 或者 ext3 以及 Windows 中的 NTFS),那么只有写入的字段会占用空间。使用 qemu-img info 了解 Unix/Linux 中映像或者 ls -ls 使用的实际大小。
qcow2
QEMU 映像格式,最万能的格式。使用它可获得较小映像(如果您的系统不支持中断,例如在 Windows 中,它会很有用)、额外的 AES 加密法、zlib 压缩以及对多 VM 快照的支持。
qcow
旧的 QEMU 映像格式。只用于与旧版本兼容。
cow
写入映像格式的用户模式 Linux 副本。包含 cow 格式的目的只是为了与前面的版本兼容。它无法在 Windows 中使用。
vmdk
VMware 3 和 4 兼容映像格式。
cloop
Linux 压缩回送映像,只有在重复使用直接压缩的 CD-ROM 映像时有用,比如在 Knoppix CD-ROM 中。
例如:
将img文件转化为qcow2_cow文件形式:
qemu-img convert root.img -O qcow2 kvm-centos-5.4-64-weibo_duilie_php5.2.14.qcow2_cow
反之一样。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。