在编写驱动过程分析中会遇到许多难找的问题,这时候调试的方法就很重要了,下面介绍的是利用打印的方法调试驱动,这种方法同样可以用在应用的调试过程中,而且很有效。
1、prink的原理
首先介绍一下打印的函数prink的原理,printk的原理是最终打印在终端上的。所以只要是能成为终端的设备均可被打印,比如串口、网络、LCD等等。
在u-boot的启动参数中,有这么一项console=ttySAC0,其中ttySAC0就是最终printk打印到的设备。
bootargs=console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.101:/work/nfs_andy/first_fs ip=192.168.1.18:192.168.1.101:192.168.1.1:255.255.255.0::eth0:off
1.1、__setup调用过程分析
为了分析prink,可以在内核源码中搜索“console=”,最终可以在在kernelprintk.c中找到__setup('console=', console_setup);这种形式的定义在Linux内核源码阅读记录一之分析存储在不同段中的函数调用过程中已经介绍过,下面介绍一遍调用过程,__setup被定义在includelinuxinit.h中:
171 #define __setup(str, fn)
172 __setup_param(str, fn, fn, 0)
160 #define __setup_param(str, unique_id, fn, early)
161 static char __setup_str_##unique_id[] __initdata = str;
162 static struct obs_kernel_param __setup_##unique_id
163 __attribute_used__
164 __attribute__((__section__('.init.setup')))
165 __attribute__((aligned((sizeof(long)))))
166 = { __setup_str_##unique_id, fn, early }
148 struct obs_kernel_param {
149 const char *str;
150 int (*setup_func)(char *);
151 int early;
152 };
最终把__setup('console=', console_setup);展开可以得到:从展开的函数可以知道,最终定义了一个位于.init.setup段的结构体__setup_console_setup,并且初始化了它的各个成员,有函数,有名称等等。
static char __setup_str_console_setup[] __initdata = 'console=';
static struct obs_kernel_param __setup_console_setup
__attribute_used__
__attribute__((__section__('.init.setup')))
__attribute__((aligned((sizeof(long)))))
=
{
__setup_str_console_setup,
console_setup,
0
}
接着在arch/arm/kernel/vmlinux.lds中搜索.init.setup,可以得到这个段的初始化地址与结束地址__setup_start、__setup_end
__setup_start = .;
*(.init.setup)
__setup_end = .;
为了得到调用这个段的时机,我们继续接着在内核源码中搜索__setup_start,在initmain.c中的obsolete_checksetup函数搜索到了它,obsolete_checksetup这个函数的第6行开始会根据__setup_console_setup结构体中的str字符串值与传入的line字符串值是否相等以及early参数来决定是否调用__setup_console_setup结构体中的函数。
1 static int __init obsolete_checksetup(char *line)
2 {
3 struct obs_kernel_param *p;
4 int had_early_param = 0;
5
6 p = __setup_start;//.init.setup的首地址
7 do {
8 int n = strlen(p->str);
9 if (!strncmp(line, p->str, n)) {//在.init.setup中寻找相符的命令行参数
10 if (p->early) {//如果early大于0,那么这个参数在前面已经处理过了
11 /* Already done in parse_early_param?
12 * (Needs exact match on param part).
13 * Keep iterating, as we can have early
14 * params and __setups of same names 8( */
15 if (line[n] == '