early_param和__setup宏

一.宏的定义

在/include/linux/Init.h中

#define __setup(str, fn)     	\
 __setup_param(str, fn, fn, 0)

 

#define early_param(str, fn)	\
__setup_param(str, fn, fn, 1)

两个宏都会调用__setup_param


跟踪进__setup_param宏的定义

#define __setup_param(str, unique_id, fn, early)			\
	static const char __setup_str_##unique_id[] __initconst	\
		__aligned(1) = str; 				\
	static struct obs_kernel_param __setup_##unique_id		\
		__used __section(.init.setup)			\
		__attribute__((aligned((sizeof(long)))))		\
		= { __setup_str_##unique_id, fn, early }


 这个宏里面有个结构体obs_kernel_param

struct obs_kernel_param {
	const char *str;
	int (*setup_func)(char *);
	int early;
};

 

结合上面两个宏和一个结构体展开__setup

__setup(str, fn)宏定义了

一个static const char  __setup_str_fn[]变量=str

接着定义了

一个static struct obs_kernel_param __setup_fn结构体,并赋值(标记编译进.init.setup段)

{

    str;

    fn(char *); 

    0,或1

}

二.宏的作用

1.编译相关

在/include/asm-generic/Vmlinux.lds.h文件中定义了__setup_start.....__setup_end段

#define INIT_SETUP(initsetup_align)			\
		. = ALIGN(initsetup_align);		\
		VMLINUX_SYMBOL(__setup_start) = .;	\
		*(.init.setup)			\
		VMLINUX_SYMBOL(__setup_end) = .;

标记了.init.setup的函数会被编译进该段

2.内核启动的相关调用关系

在start_kernel中调用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;
	strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);	//复制启动命令行数据
	parse_early_options(tmp_cmdline);	//调用parse_early_options函数
	done = 1;
}

parse_early_options函数

void __init parse_early_options(char *cmdline)
{
	parse_args("early options", cmdline, NULL, 0, do_early_param);
}

接着调用parse_args函数

int parse_args(const char *name,char *args,const struct kernel_param *params,unsigned num,int (*unknown)(char *param, char *val))
{
	char *param, *val;
	DEBUGP("Parsing ARGS: %s\n", args);

	args = skip_spaces(args);

	while (*args) {	//遍历启动命令行
		int ret;
		int irq_was_disabled;

		args = next_arg(args, &param, &val);	//获取下一个参数,填充param和val参数(例如:param--console;val--tty2,115200n8)
		irq_was_disabled = irqs_disabled();
		ret = parse_one(param, val, params, num, unknown);	//解析一个命令行参数
		if (irq_was_disabled && !irqs_disabled()) {
			printk(KERN_WARNING "parse_args(): option '%s' enabled ""irq's!\n", param);
		}
		switch (ret) {
		case -ENOENT:
			printk(KERN_ERR "%s: Unknown parameter `%s'\n",name, param);
			return ret;
		case -ENOSPC:
			printk(KERN_ERR "%s: `%s' too large for parameter `%s'\n",name, val ?: "", param);
			return ret;
		case 0:
			break;
		default:
			printk(KERN_ERR"%s: `%s' invalid for parameter `%s'\n",name, val ?: "", param);
			return ret;
		}
	}

	/* All parsed OK. */
	return 0;
}

命令行参数的解析parse_one

static int parse_one(char *param,char *val,const struct kernel_param *params,unsigned num_params,int (*handle_unknown)(char *param, char *val))
{
	unsigned int i;
	int err;

	/* Find parameter */
	for (i = 0; i < num_params; i++) {	//num_params=0
		if (parameq(param, params[i].name)) {
			if (!val && params[i].ops->set != param_set_bool)
				return -EINVAL;
			DEBUGP("They are equal!  Calling %p\n",params[i].ops->set);
			mutex_lock(&param_lock);
			err = params[i].ops->set(val, &params[i]);
			mutex_unlock(&param_lock);
			return err;
		}
	}

	if (handle_unknown) {	//若handle_unknown函数存在
		DEBUGP("Unknown argument: calling %p\n", handle_unknown);
		return handle_unknown(param, val);	//则调用handle_unknown函数,参数为param,val
	}

	DEBUGP("Unknown argument `%s'\n", param);
	return -ENOENT;
}

回溯回去handle_unknow函数就是do_early_param

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

	for (p = __setup_start; p < __setup_end; p++) {
		if ((p->early && strcmp(param, p->str) == 0) || (strcmp(param, "console") == 0 && strcmp(p->str, "earlycon") == 0)) {
			if (p->setup_func(val) != 0)
				printk(KERN_WARNING"Malformed early option '%s'\n", param);
		}
	}
	/* We accept everything at this stage. */
	return 0;
}

do_early_param函数从__setup_start遍历到__setup_end段,

判断参数,进入if函数体里面

if (p->setup_func(val) != 0)这句调用了对应setup_func或early_param成员的函数,并将val作为其参数,val其实便是__setup(str, fn)或__early_param中的str

其实就是调用了fn(str)
这里的第一条if会刷选掉__setup定义的情况(除了console和earlycon参数的),因为__setup定义的obs_kernel_param结构体p->early=0

__setup定义的fn会在start_kernel->parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);

unknown_bootoption->obsolete_checksetup函数给调用
看start_kernel中调用顺序

	parse_early_param();
	parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param,&unknown_bootoption);

可见先调用__early_param定义的解析参数函数及__setup定义的(console及earlycon)的参数解析函数

接着再调用__setup定义的其他解析参数函数



 

 

你可能感兴趣的:(early_param和__setup宏)