172 __setup_param(str, fn, fn, 0)
先看__setup_param。它定义了两个参数,一个是char型的字符串__setup_str_##unique_id,另外一个为obs_kernel_param 结构体,它位于includelinuxInit.h。obs_kernel_param 结构体位于
.init.setup段,它的str参数即为__setup_str_##unique_id。__setup宏调用__setup_param传入两个参数str与fn,代表命令行名字与处理函数。
148 struct obs_kernel_param {
149 const char *str;
150 int (*setup_func)(char *);
151 int early;
152 };
2)、root=/dev/mtdblock3参数解析过程
回到initMain.c 中的start_kernel函数继续分析
525 setup_arch(&command_line);//返回的command_line是还未处理的命令行参数存放的首地址
526 setup_command_line(command_line);//static_command_line存放未处理的命令行参数,saved_command_line存放所有的命令行参数
544 printk(KERN_NOTICE 'Kernel command line: %sn', boot_command_line);//打印命令行参数
545 parse_early_param();//一些前期代码的初始化
546 parse_args('Booting kernel', static_command_line, __start___param,
547 __stop___param - __start___param,
548 &unknown_bootoption);//后续的命令处理
其中parse_early_param函数是对一些early属性的命令后做解析,它位于.early_param.init段,包括:cachepolicy=、nocache、nowb、ecc=、initrd=、mem=等等,我们的参数没有涉及到这类命令,所以不去细细的分析这个函数了。
重点关注parse_args函数,先分析函数的参数:
static_command_line :存放未处理的命令行参数首地址
__start___param : 内核参数的存放地址,它处于__param段
__stop___param - __start___param : 内核参数大小
unknown_bootoption : 处理函数
接着看到parse_args函数内部阶段,它位于kernelParams.c 下,可以看到在这里会将所有命令行处理完成。
144 while (*args) {//循环处理剩余的命令行,直到全部处理完成
145 int ret;
146 int irq_was_disabled;
147
148 args = next_arg(args, ¶m, &val);//找出下一个命令行参数*param为命令名称,*val为参数值
149 irq_was_disabled = irqs_disabled();
150 ret = parse_one(param, val, params, num, unknown);//处理
接着看到处理函数parse_one,它位于kernelParams.c 下。这里面还判断了一个内核的参数,我们传入的参数没有内核参数,内核参数存在于__param段,有:nousb、block2mtd_setup等等,我们传入的命令行参数没有内核参数,所以不关心
49 static int parse_one(char *param,
50 char *val,
51 struct kernel_param *params,
52 unsigned num_params,
53 int (*handle_unknown)(char *param, char *val))
54 {
55 unsigned int i;
56
57 /* Find parameter */
58 for (i = 0; i < num_params; i++) {//从__param段找出与命令行参数相同的名字
59 if (parameq(param, params[i].name)) {
60 DEBUGP('They are equal! Calling %pn',
61 params[i].set);
62 return params[i].set(val, ¶ms[i]);//如果是内核的参数,那么直接传给内核参数,然后返回。
63 }
64 }
65
66 if (handle_unknown) {//如果不是内核的参数,并且处理函数存在
67 DEBUGP('Unknown argument: calling %pn', handle_unknown);
68 return handle_unknown(param, val);//调用处理函数处理
69 }
70
71 DEBUGP('Unknown argument `%s'n', param);
72 return
接着看到handle_unknown函数,即unknown_bootoption函数,它位于initMain.c中,截取其中的一段程序
260 /* Change NUL term back to '=', to make 'param' the whole string. */
261 if (val) {//如果val不为空,做一些处理
262 /* param=val or param='val'? */
263 if (val == param+strlen(param)+1)
264 val[-1] = '=';
265 else if (val == param+strlen(param)+2) {
266 val[-2] = '=';
267 memmove(val-1, val, strlen(val)+1);
268 val--;
269 } else
270 BUG();
271 }
272
273 /* Handle obsolete-style parameters */
274 if (obsolete_checksetup(param))
275 return 0;
接着看到obsolete_checksetup函数,它同样位于initMain.c中,这个函数大致的意思就是在.init.setup中找到符合的命令行参数,如果不是前期已经处理的参数(即early值为0的参数,那么调用处理函数处理它。它由__setup宏定义或者__setup_null_param宏定义(这两个宏定义前面已经介绍过了),搜索一下这两个宏定义,发现了__setup('root=', root_dev_setup);、__setup('init=', init_setup);、__setup('console=', console_setup);都在这里面被处理。
190 static int __init obsolete_checksetup(char *line)
191 {
192 struct obs_kernel_param *p;
193 int had_early_param = 0;
194
195 p = __setup_start;//.init.setup的首地址
196 do {
197 int n = strlen(p->str);
198 if (!strncmp(line, p->str, n)) {//在.init.setup中寻找相符的命令行参数
199 if (p->early) {//如果early大于0,那么这个参数在前面已经处理过了
200 /* Already done in parse_early_param?
201 * (Needs exact match on param part).
202 * Keep iterating, as we can have early
203 * params and __setups of same names 8( */
204 if (line[n] == '