📄 7.htm
字号:
49 #ifdef CONFIG_X86_REMOTE_DEBUG <br>
50 gdb_debug_hook * linux_debug_hook; <br>
前面的条件宏定义是Linux内核的编译选项,在自定义内核编译选项的时候,选中KGDB的支 <br>
持,这个宏被定义。 <br>
 static void gdb_interrupt(int irq, void* dev_id, struct pt_regs* reg <br>
<br>
); <br>
gdb_interrupt()函数是相对于该串口的中断服务程序,通过这个中断服务程序利用read_ <br>
data_bfr()从串口读取数据,填充缓冲区。 <br>
 int gdb_hook(void); <br>
该函数调用gdb_serial_setup()函数初始化串口,然后向系统注册中断服务程序gdb_inte <br>
rrupt,运行set_debug_traps()函数,设置断点。在这个函数的运行过程中取得和本地主 <br>
机的连接。 <br>
6.2.3 内核进入调试状态的路径 <br>
让内核进入调试状态有两种方法,一种是通过在内核启动的时候向内核传入参数,这个时 <br>
候可以调试系统启动过程内核的运行状况;另外一种方法是在内核完全导入系统正常运行 <br>
的情况下,通过使用一个gdbstart工具将驱动串口设备,内核的控制权交给本地主机,从 <br>
而进入系统调用的调试状态。可以通过它监控每一个系统调用在内核的运行过程。 <br>
6.2.3.1 系统启动的时候向内核传递参数 <br>
我们知道,在Linux启动的时候,可以给Linux内核传递进去一些参数,Linux内核在运行时 <br>
也就可以根据传入的入口参数决定运行时的一些状况。比如说,给内核传递root=/dev/hd <br>
a4,就表示内核在启动之后,把/dev/hda4作为根文件系统装载上来。对于KGDB,定义了三 <br>
个可以向内核传递的参数,用来指定本地主机和远程主机进行连接的方法。这三个参数是 <br>
gdb,gdbttyS和gdbbaud。 <br>
gdb参数表示在内核启动的时候就需要进行内核的调试,也就是说,内核并不会自己运行完 <br>
,而是在一定的时候会停下来,将控制权交给远程的GDB来控制执行。 <br>
gdbttyS是用来指定本地机器和远程主机需要连接的串口号。gdbbaud是该串口连接的数据 <br>
传输波特率。在串口上进行连接的时候,需要双方的波特率相同才可能正确连接。这两个 <br>
参数需要在后面调用gdb_serial_setup()函数将指定的串口初始化。 <br>
然后,系统进入gdb_hook()函数,调用gdb_serial_setup()函数和注册中断处理函数,并 <br>
且运行set_debug_traps()交出控制权,从而进行内核启动过程的调试。 <br>
整个过程可以参看下面的源代码: <br>
1)init/main.c 中的一段: <br>
———————————————————————————init/main.c— <br>
394 #ifdef CONFIG_X86_REMOTE_DEBUG <br>
395 if (!strcmp(line,"gdb")) {/*判断内核参数gdb*/ <br>
396 gdb_enter = 1; <br>
397 continue; <br>
398 } <br>
399 if (!strcmp(line,"gdbttyS=")) { <br>
400 gdb_ttyS = simple_strtoul(line+8,NULL,10); <br>
401 continue; <br>
402 } <br>
402 } <br>
403 if (!strcmp(line,"gdbbaud=")) { <br>
404 gdb_baud = simple_strtoul(line+8,NULL,10); <br>
405 continue; <br>
406 } <br>
407 #endif /* CONFIG_X86_REMOTE_DEBUG */ <br>
———————————————————————————————— <br>
2)gdb_hook()函数: <br>
该函数定义在drivers/char/gdbserial.c中,下面是它的一些片断: <br>
—————————————————————————gdbserial.c—— <br>
170 int gdb_hook(void) <br>
…… <br>
178 if((ser = gdb_serial_setup(gdb_ttyS, gdb_baud)) == 0) { <br>
…… <br>
183 gdb_port = ser->port; <br>
184 gdb_irq = ser->irq; <br>
185 <br>
186 if (ser->info != NULL) <br>
187 { <br>
…… <br>
193 gdb_serial_setup(gdb_ttyS, gdb_baud) ; <br>
194 } <br>
195 <br>
195 <br>
196 retval = request_irq(gdb_irq, <br>
197 gdb_interrupt, <br>
198 SA_INTERRUPT, <br>
199 "GDB-stub", NULL); <br>
…… <br>
211 set_debug_traps() ; <br>
…… <br>
217 printk("Waiting for connection from remote gdb... ") ; <br>
218 breakpoint() ; <br>
219 gdb_null() ; <br>
220 <br>
221 printk("Connected.\n"); <br>
222 <br>
223 return(0) ; <br>
224 <br>
225 } /* gdb_hook_interrupt2 */ <br>
———————————————————————————————— <br>
<br>
后来的处理都交给handle_exception()函数了,handle_exception和主机的GDB连接,获 <br>
取控制信息,然后控制本地的内核的运行。 <br>
6.2.3.2 使用gdbstart将系统控制权交出 <br>
使用gdbstart是让系统内核交出控制权的另一种方法,通过给gdbstart传入需要连接的串 <br>
口的两个参数信息(串口号和传输波特率),在内核的控制权交出之后和本地的GDB建立连 <br>
接,从而可以监控每一次进入系统调用的时候在内核运行的情况。gdbstart是一个由KGDB <br>
提供的工具,源程序放在arch/i386/kernel/gdbstart.c里面。 <br>
这种交出控制权的方法和刚才所描述的不同,它是采用ioctl系统调用进入的。对需要连接 <br>
的串口调用ioctl系统调用,引发初始化串口和进入gdb_hook()函数。 <br>
在gdbstart.c里面: <br>
——————————————————————————gdbstart.c—— <br>
112 fil = open(tty_name, O_RDWR) ;//打开串口设备 <br>
…… <br>
129 sync() ; <br>
130 rslt = ioctl(fil, TIOCGDB, 0);//进入ioctl过程 <br>
…… <br>
———————————————————————————————— <br>
进入到ioctl过程中后,程序运行到drivers/char/serial.c中的判断如何对串口进行控 <br>
<br>
的过程,在这里,给kgdb需要使用的串口信息赋值,并且进入gdb_hook(),开始了内核 <br>
的调试。 <br>
——————————————————————drivers/char/serical.c—— <br>
2479 #ifdef CONFIG_X86_REMOTE_DEBUG <br>
2480 case TIOCGDB: <br>
2481 gdb_ttyS = MINOR(tty->device) & 0x03F ; <br>
2482 gdb_baud = tty_get_baud_rate(tty) ; <br>
2483 return gdb_hook(); <br>
2484 #endif <br>
———————————————————————————————— <br>
在需要进行调试的时候,运行gdbstart [-s speed] [-t tty-dev]命令,就可以和本地的 <br>
gdb的remote方式连接,从而进入调试过程。当然在gdb里面需要设定对应的串口和同样的 <br>
传输波特率才行。 <br>
<br>
6.2.4 Linux内核调试过程示例 <br>
这里给出一次Linux内核调试过程示例,对这种内核调试环境也有一种直观的理解。 <br>
在远程主机上,建立一个脚本文件 debug,用于启动远程主机上的stub程序,交出内核运 <br>
行的控制权。 <br>
————————————————————————debug script——— <br>
#!/bin/sh <br>
gdbstart -s 38400 -t /dev/ttyS0 <<EOF <br>
<br>
EOF <br>
———————————————————————————————— <br>
表示通过/dev/ttyS0,波特率为38400的速度进行连接。 <br>
这个时候,在本地主机运行gdb,如下所示(vmlinux是内核符号表): <br>
然后就可以随意使用gdb的命令了。比如说需要打印出外部变量jiffies的值,那么运行 <br>
<br>
[root@cims /usr/src/linux/]#gdb vmlinux <br>
GNU gdb 19991004 <br>
Copyright 1998 Free Software Foundation, Inc. <br>
GDB is free software, covered by the GNU General Public License, and you are <br>
<br>
welcome to change it and/or distribute copies of it under certain conditions. <br>
<br>
Type "show copying" to see the conditions. <br>
There is absolutely no warranty for GDB. Type "show warranty" for details. <br>
This GDB was configured as "i386-redhat-linux"... <br>
(gdb)set remotebaud 38400 <br>
(gdb)target remote /dev/ttyS0 <br>
breakpoint() at i386-stub.c:750 <br>
750 } <br>
(gdb) <br>
(gdb)p jiffies <br>
$1 = 0x23ab <br>
…… …… <br>
又假如说想知道当前的进程是什么(使用的是task_struct变量),地址存放在eip寄存 <br>
<br>
中。那么可以如下操作: <br>
(gdb)info registers <br>
…… …… <br>
…… …… <br>
eip: 0xc014b0e8 –1072385816 <br>
…… …… <br>
(gdb)p (struct task_struct)*0xc014b0e8 <br>
$2 = {state = 0xbffffaa8, flags = 0xc010bf64, sigpending = 0xbffffccf, <br>
addr_limit = {seg = 0xbffffb70}, exec_domain = 0xbffffb60, need_resched = 0x0, <br>
<br>
counter = 0xbffffccf, priority = 0xbffffaa8, avg_slice = 0xa, has_cpu = 0x2b, <br>
<br>
…… …… <br>
如果想调试系统调用的情况,可以在系统调用的入口设置断点。假如需要调试unlink系 <br>
<br>
调用,那么可以如下操作: <br>
(gdb)break sys_unlink <br>
Breakpoint 1 at 0xc014b0e8: file /usr/src/linux-2.3.35/include/asm/current.h, <br>
line 9. <br>
(gdb)continue <br>
这个命令让远程机器上的内核运行下去,然后可以在远程机器上触发一个unlink的系统 <br>
<br>
用,比如说删除一个文件。当发生这件事情的时候,系统遇到断点,交出控制权,进入如 <br>
下界面: <br>
(gdb)continue <br>
Continuing. <br>
Continuing. <br>
<br>
Breakpoint 2, sys_unlink (pathname=0xbffffccf "mm") <br>
at /usr/src/linux-2.3.35/include/asm/current.h:9 <br>
9 __asm__("andl %%esp,%0; ":"=r" (current) : "0" (~8191UL)); <br>
触发了断点,就可以进行系统调用在内核的调试了。 <br>
6.3 RTLinux的调试环境的开发设想 <br>
根据KGDB的设计,可以考虑如何在RTLinux平台上搭建调试平台。在RTLinux上搭建调试平 <br>
台和Linux内核调试不同,这里要进行的是调试RT Linux的实时任务,这里的调试就应该像 <br>
是对一般可执行程序的调试一样,对插入的模块进行调试,检查监视每一个实时任务线程 <br>
的情况。 <br>
这里,我提出一种可以用来对实时任务进行调试的方案,利用RTLinux的特性,可以作如下 <br>
的设计: <br>
1. 使用RT Linux的FIFO设备作为调试的连接设备。gdb使用远程调试的方法,和一个FIFO <br>
相连接,从FIFO中获取数据,并且通过FIFO设备发出实时任务运行下去指令。 <br>
2. 针对FIFO设备实现基本的stub必要的函数接口,就是在6.1.3提供的那些函数如getDeb <br>
ugChar(),putDebugChar()等等。然后实现set_debug_traps()、breakpoint()、handle_ <br>
exception()等函数。 <br>
3. 在需要调试的实时任务模块中,使用breakpoint()函数增加一个断点。在运行到break <br>
point()的时候,触发一个中断,进入中断处理程序,从而将实时任务模块的控制权交出。 <br>
<br>
<br>
<br>
这里仍然是根据GDB的远程调试功能的构思,编写出适合于RTLinux 实时任务的调试环 <br>
<br>
,对使用RT Linux进行实时环境下的编程很有帮助。 <br>
<br>
6.4 小结 <br>
本章详细介绍了如何利用GDB搭建Linux内核的远程调试环境,从而进行Linux内核的调试过 <br>
程。从GDB的原理到Kernel Debugger的实现,到在RT Linux环境下的调试环境的建立的设 <br>
想。 <br>
调试环境是基于Linux的嵌入式系统开发工具链的重要部分,使用该调试环境,是实现内核 <br>
个性化定制的必要条件。 <br>
<br>
-- <br>
紅酥手 黃滕酒 滿城春色尃m澚 <br>
|風惡 歡情薄 一懷愁緒 幾年離索 <br>
錯 錯 錯 ! <br>
春如舊 人空瘦 満I奂t 捧o綃透 <br>
桃花落 槌f亻w 山盟雖在 鍟\
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -