参考链接:https://blog.csdn.net/skyflying2012/article/details/41142801
(1)早期参数解析:
parse_early_param(init/main.c)—>parse_early_options(tmp_cmdline)—>
parse_args(“early options”, cmdline, NULL, 0, 0, 0, do_early_param);
parse_args函数实现如下:
.路径:/kernel/params.c
char *parse_args(const char *doing,
char *args,
const struct kernel_param *params,
unsigned num,
s16 min_level,
s16 max_level,
int (*unknown)(char *param, char *val, const char *doing))
{
char *param, *val;
/* Chew leading spaces */
args = skip_spaces(args);
if (*args)
pr_debug("doing %s, parsing ARGS: '%s'\n", doing, args);
while (*args) {
int ret;
int irq_was_disabled;
/*args = next_arg(args, ¶m, &val);//这个函数就是获取解析一个值,
*param=args指向原始的args地址
*val是解析一个等号的值
*函数返回解析完一个等号的值后的下个参数首地址*/
args = next_arg(args, ¶m, &val);
/* Stop at -- */
if (!val && strcmp(param, "--") == 0)
return args;
irq_was_disabled = irqs_disabled();
//然后利用parse_one来一个个解析参数的意义
ret = parse_one(param, val, doing, params, num,
min_level, max_level, unknown);
if (irq_was_disabled && !irqs_disabled())
pr_warn("%s: option '%s' enabled irq's!\n",
doing, param);
switch (ret) {
case -ENOENT:
pr_err("%s: Unknown parameter `%s'\n", doing, param);
return ERR_PTR(ret);
case -ENOSPC:
pr_err("%s: `%s' too large for parameter `%s'\n",
doing, val ?: "", param);
return ERR_PTR(ret);
case 0:
break;
default:
pr_err("%s: `%s' invalid for parameter `%s'\n",
doing, val ?: "", param);
return ERR_PTR(ret);
}
}
/* All parsed OK. */
return NULL;
}
然后利用parse_one来一个个解析参数的意义
ret = parse_one(param, val, doing, params, num,min_level, max_level, unknown);//unknown指向parse_args传进来的实参do_early_param
我们来看下parse_one的定义
static int parse_one(char *param,
char *val,
const char *doing,
const struct kernel_param *params,
unsigned num_params,
s16 min_level,
s16 max_level,
int (*handle_unknown)(char *param, char *val,
const char *doing))
{
unsigned int i;
int err;
/* Find parameter 在早期的时候并不需要,但是在driver的时候可以*/
for (i = 0; i < num_params; i++) {
pr_debug("--flyaudio test --doing %s:\n", doing);
if (parameq(param, params[i].name)) {
if (params[i].level < min_level
|| params[i].level > max_level)
return 0;
/* No one handled NULL, so do it here. */
if (!val &&
!(params[i].ops->flags & KERNEL_PARAM_OPS_FL_NOARG))
return -EINVAL;
pr_debug("handling %s with %p\n", param,
params[i].ops->set);//这里便是设置参数的值
mutex_lock(¶m_lock);
param_check_unsafe(¶ms[i]);
err = params[i].ops->set(val, ¶ms[i]);
mutex_unlock(¶m_lock);
return err;
}
}
if (handle_unknown) {
pr_debug("doing %s: %s='%s'\n", doing, param, val);
return handle_unknown(param, val, doing);//直接调用调用 do_early_param解析参数
}
pr_debug("Unknown argument '%s'\n", param);
return -ENOENT;
}//handle_unknown是在parse_args("early options", cmdline, NULL, 0, 0, 0, do_early_param)的参数do_early_param函数的地址
然后do_early_param函数的执行,看下do_early_param的定义
static int __init do_early_param(char *param, char *val, const char *unused)
{
const struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if ((p->early && parameq(param, p->str)) ||
(strcmp(param, "console") == 0 &&
strcmp(p->str, "earlycon") == 0)
) {
if (p->setup_func(val) != 0)
pr_notice("Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
参考上面的链接的说法就是
kernel有一个Init.setup的区域,地址__setup_start到__setup_end
这个地址区域存了一份表(include/linux/init.h),这份表每个编号都是以一个
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};结构体的形式存在.
const char *str;代表的是参数的关键字眼
int (*setup_func)(char *);是该设置参数的值函数,并具体的实现
例如;
static int __init console_setup(char *str)
{
char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for "ttyS" */
char *s, *options, *brl_options = NULL;
int idx;
if (_braille_console_setup(&str, &brl_options))
return 1;
/*
* Decode str into name, index, options.
*/
if (str[0] >= '0' && str[0] <= '9') {
strcpy(buf, "ttyS");
strncpy(buf + 4, str, sizeof(buf) - 5);
} else {
strncpy(buf, str, sizeof(buf) - 1);
}
buf[sizeof(buf) - 1] = 0;
options = strchr(str, ',');
if (options)
*(options++) = 0;
#ifdef __sparc__
if (!strcmp(str, "ttya"))
strcpy(buf, "ttyS0");
if (!strcmp(str, "ttyb"))
strcpy(buf, "ttyS1");
#endif
for (s = buf; *s; s++)
if (isdigit(*s) || *s == ',')
break;
idx = simple_strtoul(s, NULL, 10);
*s = 0;
__add_preferred_console(buf, idx, options, brl_options);
console_set_on_cmdline = 1;
return 1;
}
__setup(“console=”, console_setup);
__setup这个宏展开后就是把console=参数对应console_setup函数
module_param展开后对应
Static struct kernel_param __moduleparam_const __param_use_acm
__used __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) = {
.name = MODULE_PARAM_PREFIX#use_acm,
.ops = param_ops_bool,
.Perm=0,
.level = -1.
.arg = &use_acm
}
ops=param_ops_bool,是kernel_param_ops结构体,定义如下:
struct kernel_param_ops param_ops_bool = {
.set = param_set_bool,
.get = param_get_bool,
};
会像.init.setup一样放在某一个数据段,对应这一份表