内核中对cmdline的解析

1、__setup宏

参考博客:。

2、内核中解析cmdline的函数调用关系

start_kernel()
		parse_early_param()
			parse_early_options()
				parse_args();
					next_arg()
					parse_one()
						do_early_param()

3、parse_early_param()函数

void __init parse_early_param(void)
{
          
   
	static __initdata int done = 0;
	static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];

	if (done)
		return;

	/* All fall through to do_early_param. */
	strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
	parse_early_options(tmp_cmdline);
	done = 1;
}
parse_early_param()函数调用parse_early_options()函数,并将cmdline传入。boot_command_line变量保存的是uboot传递给内核的cmdline,具体是在内核解析tag参数时赋值。

4、parse_early_options()函数

void __init parse_early_options(char *cmdline)
{
          
   
	parse_args("early options", cmdline, NULL, 0, do_early_param);
}
调用parse_args()函数并将cmdline和do_early_param函数指针传入。

5、parse_args()函数

int parse_args(const char *name,
	       char *args,
	       struct kernel_param *params,
	       unsigned num,
	       int (*unknown)(char *param, char *val))
{
          
   
	char *param, *val;
	
	/* Chew leading spaces */
	args = skip_spaces(args);

	while (*args) {
          
   
		int ret;
		int irq_was_disabled;

		args = next_arg(args, &param, &val);//解析cmdline的格式解析出cmdline的每个单元
		irq_was_disabled = irqs_disabled();
		ret = parse_one(param, val, params, num, unknown);
		if (irq_was_disabled && !irqs_disabled()) {
          
   
			······
		}
		switch (ret) {
          
   
			······
		}
	}
	return 0;
}
(1)next_arg()函数:按照cmdline的格式逐个解析; 比如cmdline=“root=/dev/mmcblk0p2 rootfstype=ext3” 第一次解析结果:param=“root”,val=“/dev/mmcblk0p2” 第二次解析结果:param=“rootfstype”,val=“ext3” (2)parse_one()函数: 对每个从cmdline解析出的单元进行处理;

6、parse_one()函数

static int parse_one(char *param,
		     char *val,
		     struct kernel_param *params, 
		     unsigned num_params,
		     int (*handle_unknown)(char *param, char *val))
{
          
   
	······
	//handle_unknown函数指针就是do_early_param()函数
	if (handle_unknown) {
          
   
		DEBUGP("Unknown argument: calling %p
", handle_unknown);
		return handle_unknown(param, val);
	}

	DEBUGP("Unknown argument `%s
", param);
	return -ENOENT;
}
对next_arg()函数解析出的param和val进行处理,handle_unknown函数指针就是do_early_param()函数。具体的解析工作都是do_early_param()函数做的。

7、do_early_param()函数

static int __init do_early_param(char *param, char *val)
{
          
   
	struct obs_kernel_param *p;

	for (p = __setup_start; p < __setup_end; p++) {
          
    //遍历".init.setup"段,寻找匹配的obs_kernel_param结构体
		if ((p->early && strcmp(param, p->str) == 0) ||
		    (strcmp(param, "console") == 0 &&
		     strcmp(p->str, "earlycon") == 0)
		) {
          
   
			if (p->setup_func(val) != 0)	//匹配到obs_kernel_param结构体就执行绑定的处理函数
				printk(KERN_WARNING
				       "Malformed early option %s
", param);
		}
	}
	/* We accept everything at this stage. */
	return 0;
}
(1)__setup_start和__setup_end是".init.setup"段的起始/结束地址; (2)".init.setup"段的遍历结合__setup宏和链接脚本进行理解;
经验分享 程序员 微信小程序 职场和发展