最近在调试程序的时候遇到linux内核链表结构的问题. 因为应用程序中大量使用了链表, 但是由于其特性, 导致在调试的时候十分不方便. 为了方便以后的处理, 决定解决一下这个问题.
有关linux内核链表的说明推荐一下这篇文章 , 讲的蛮好的.
解决方案
搜索一下, 发现还有不少同学有讲到这个问题的, 这里推荐两个现成的方案:
一个现成的gdb脚本
一个非常详细的解析
两个链接都是通过gdb脚本来实现的. 对gdb脚本感兴趣的同学不妨看一下这两个教程(貌似是用ada做的例子): part1 和part2 .
尝试一下
学习了以上的方法, 自己尝试一下. 先写个简单的测试小程序:
aptx.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include "list.h"
typedef struct fun_s {
struct list_head list ;
int data ;
} fun_t ;
int
main ( void )
{
struct list_head fun_list ;
fun_t * f ;
fun_t fun [] = {
{ { NULL , NULL }, 4 },
{ { NULL , NULL }, 8 },
{ { NULL , NULL }, 6 },
{ { NULL , NULL }, 9 },
{ { NULL , NULL }, - 1 },
};
INIT_LIST_HEAD ( & fun_list );
for ( f = fun ; f -> data != - 1 ; f ++ ) {
list_add_tail ( & f -> list , & fun_list );
}
list_for_each_entry ( f , & fun_list , list ) {
printf ( "%d \n " , f -> data );
}
return 0 ;
}
好, 然后根据上面google出的两个链接的相关代码, 自己写个对应的gdb脚本:
test.gdb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
define find_next_node
set $_list = $arg0
set $offset = ( char * ) & (( fun_t * ) 0 ) -> list - ( char * ) 0
set $node = ( fun_t * )(( char * ) $_list -> next - $offset )
if & $node -> list == $list_head
set $node = 0
end
end
define dump_fun_list
# $arg0 should be the address of the list_head
set $list_head = $arg0
set $loop = $arg0
find_next_node $loop
while $node
p * $node
set $loop = & $node -> list
find_next_node $loop
end
end
break 30
这里需要提一下, gdb启动的时候会默认加载本地.gdbinit
文件, 但是不太推荐这么做(因为我的环境默认禁止了^_^). 我们可以通过-x gdb_script_file
来指定加载对应的脚本.
接下来我们直接在gdb中调用dump_fun_list &fun_list
即可以看到对应的链表情况了.