static __init int dma_debug_cmdline(char *str)
{
if (!str)
return -EINVAL;
if (strncmp(str, "off", 3) == 0) {
pr_info("debugging disabled on kernel command line\n");
global_disable = true;
}
return 1;
}
static __init int dma_debug_entries_cmdline(char *str)
{
if (!str)
return -EINVAL;
if (!get_option(&str, &nr_prealloc_entries))
nr_prealloc_entries = PREALLOC_DMA_DEBUG_ENTRIES;
return 1;
}
__setup("dma_debug=", dma_debug_cmdline);
__setup("dma_debug_entries=", dma_debug_entries_cmdline);
我们经常可以在一些模块中看到__setup(),模块利用__setup()可以解析命令行传递的参数信息,进而对模块功能进行定制化的配置。
如上所示:__setup(“dma_debug=”, dma_debug_cmdline);
假设cmdline中含有dma_debug=off
内容,则会将off
作为入参传递到dma_debug_cmdline()函数进行解析。
__setup()是一个宏,我们将其定义展开,如下所示:
/*
* NOTE: __setup functions return values:
* @fn returns 1 (or non-zero) if the option argument is "handled"
* and returns 0 if the option argument is "not handled".
*/
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
/*
* Only for really core code. See moduleparam.h for the normal way.
*
* Force the alignment so the compiler doesn't space elements of the
* obs_kernel_param "array" too far apart in .init.setup.
*/
#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") \
__aligned(__alignof__(struct obs_kernel_param)) \
= { __setup_str_##unique_id, fn, early }
以__setup(“dma_debug=”, dma_debug_cmdline);为例,我们进行代入:
static const char __setup_str_dma_debug_cmdline[] __initconst _aligned(1) = "dma_debug=";
static struct obs_kernel_param __setup_dma_debug_cmdline __used __section(".init.setup") __aligned(__alignof__(struct obs_kernel_param)) = { __setup_dma_debug_cmdline, dma_debug_cmdline, 0 }
其实这个相当于做了一件事情,只是在init.setup
段中,定义了一个struct obs_kernel_param
结构体,并为其赋值而已,赋值后的struct obs_kernel_param
结构体内容是这样的:
struct obs_kernel_param {
const char *str;
int (*setup_func)(char *);
int early;
};
obs_kernel_param.str = "dma_debug=";
obs_kernel_param.setup_func = dma_debug_cmdline;
obs_kernel_param.early = 0;
2.2 init.setup段位置
#define INIT_SETUP(initsetup_align) \
. = ALIGN(initsetup_align); \
__setup_start = .; \
KEEP(*(.init.setup)) \
__setup_end = .;
由定义可知:init.setup
段位于__setup_start - __setup_end 之间
让我们一步步从内核启动的开始进行跟踪:
start_kernel()
-> parse_args()
-> parse_one()
-> unknown_bootoption()
因为__setup()设置的obs_kernel_param.early = 0
,因此不会进入start_kernel()->parse_early_param()中去解析,另外由于__setup()将obs_kernel_param
结构体保存在__setup_start - __setup_end之中,因此在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,
void *arg,
int (*handle_unknown)(char *param, char *val,
const char *doing, void *arg))
{
unsigned int i;
int err;
/* Find parameter */
for (i = 0; i < num_params; i++) {
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);
kernel_param_lock(params[i].mod);
if (param_check_unsafe(¶ms[i]))
err = params[i].ops->set(val, ¶ms[i]);
else
err = -EPERM;
kernel_param_unlock(params[i].mod);
return err;
}
}
这部分代码不会执行,最终会来到
if (handle_unknown) {
pr_debug("doing %s: %s='%s'\n", doing, param, val);
return handle_unknown(param, val, doing, arg);
}
这里进行处理,而handle_unknown函数指针的值是&unknown_bootoption
,因此最终__setup()的内容由unknown_bootoption()
函数进行处理。
现在我们来看一些unknown_bootoption()
函数的实现细节:
/*
* Unknown boot options get handed to init, unless they look like
* unused parameters (modprobe will find them in /proc/cmdline).
*/
static int __init unknown_bootoption(char *param, char *val,
const char *unused, void *arg)
{
size_t len = strlen(param);
repair_env_string(param, val);
/* Handle obsolete-style parameters */
if (obsolete_checksetup(param))
return 0;
/* Unused module parameter. */
if (strnchr(param, len, '.'))
return 0;
if (panic_later)
return 0;
if (val) {
/* Environment option */
unsigned int i;
for (i = 0; envp_init[i]; i++) {
if (i == MAX_INIT_ENVS) {
panic_later = "env";
panic_param = param;
}
if (!strncmp(param, envp_init[i], len+1))
break;
}
envp_init[i] = param;
} else {
/* Command line option */
unsigned int i;
for (i = 0; argv_init[i]; i++) {
if (i == MAX_INIT_ARGS) {
panic_later = "init";
panic_param = param;
}
}
argv_init[i] = param;
}
return 0;
}
unknown_bootoption()
函数通过调用obsolete_checksetup()
函数进行进一步处理:
static bool __init obsolete_checksetup(char *line)
{
const struct obs_kernel_param *p;
bool had_early_param = false;
p = __setup_start;
do {
int n = strlen(p->str);
if (parameqn(line, p->str, n)) {
if (p->early) {
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '||' [ line]n== '=' )=
had_early_param ; true}
else if ( !)p->setup_funcpr_warn {
("Parameter %s is obsolete, ignored\n",)
p->str;return
; true}
else if ( setup_funcp->(+line ) n)return
; true}
++
p;}
while ( <p ) __setup_end;return
; had_early_param}
obsolete_checksetup()
从obs_kernel_param
函数中可以看到,程序将__setup_start-__setup_end中保存的所有param
结构体内容进行遍历,使用param
(param是从cmdline中获取到的参数名称)进行匹配。如果obs_kernel_param.str
和obs_kernel_param.setup_func()
相等,则会调用dma_debug=off
函数进行处理。
对于cmdline中包含obsolete_checksetup()
内容时,param=“dma_debug=”,此时就会将"off"作为参数传递到dma_debug_cmdline()函数中进行解析。
上述以__setup(“dma_debug=”, dma_debug_cmdline);为例,分析了整个解析过程。
但是还有一些细节需要注意,__setup(str, fn),对于fn函数的返回值是有要求的,当返回为1时,表示传入的参数可以被fn函数解析,如果返回为0,则代表传入的参数格式错误,无法被fn函数解析。
返回值为0的参数,会被内容所记录,流程如下:
如果fn函数的返回值为0,则look函数的返回值为false,则会由unknown_bootoption()函数进行进一步处理:
/*
* Unknown boot options get handed to init, unless they ( like
* unused parameters findmodprobe will in them ) /proc/cmdline(.
*/
static int __init unknown_bootoption)char *param, char *val,
const char *unused, void *arg=
{
size_t len ( strlen)param;(
repair_env_string)param, val;if
/* Handle obsolete-style parameters */
( (obsolete_checksetup))paramreturn
0 ;if
/* Unused module parameter. */
( (strnchr'.'param, len, ))return
0 ;if
( )panic_laterreturn
0 ;if
( )val; {
/* Environment option */
unsigned int ifor
( =i 0 ;[ envp_init]i;) i++if {
( ==i ) MAX_INIT_ENVS= {
panic_later "env" ;=
panic_param ; param}
if
( !(strncmp[param, envp_init]i)), len+1break
;}
[
envp_init]i= ; param}
else ; {
/* Command line option */
unsigned int ifor
( =i 0 ;[ argv_init]i;) i++if {
( ==i ) MAX_INIT_ARGS= {
panic_later "init" ;=
panic_param ; param}
}
[
argv_init]i= ; param}
return
0 ;}
static
如果传递的参数值不为NULL,则会将param信息保存到envp_init[]字符数组中,如果如果传递的参数值为NULL,则会将param信息保存到argv_init[]字符数组中。
最终这些保存到字符数组中的内容,则会由start_kernel()->print_unknown_bootoptions()
函数进行打印输出。
void print_unknown_bootoptions __init (void)char
{
* ;unknown_optionschar
* ;endconst
char * const* ;psize_t
; lenif
( ||panic_later ( ![argv_init1]&& ! [envp_init2]))return
;/*
* Determine how many options we have to print out, plus a space
* before each
*/
=
len 1 ;/* null terminator */ for
( =p & [argv_init1];* ;p++ p)++ {
len;+=
len strlen (*)p;}
for
( =p & [envp_init2];* ;p++ p)++ {
len;+=
len strlen (*)p;}
=
unknown_options memblock_alloc (,len) SMP_CACHE_BYTES;if
( !)unknown_optionspr_err {
("%s: Failed to allocate %zu bytes\n",__func__
,) len;return
;}
=
end ; unknown_optionsfor
( =p & [argv_init1];* ;p++ p)+=
end sprintf (,end" %s" ,* )p;for
( =p & [envp_init2];* ;p++ p)+=
end sprintf (,end" %s" ,* )p;/* Start at unknown_options[1] to skip the initial space */
pr_notice
("Unknown kernel command line parameters \"%s\", will be passed to user space.\n",&
[unknown_options1]);memblock_free
(,unknown_options) len;}
Unknown kernel command line parameters
因此__setup(str, fn) fn的返回值如果非1的话,传入的参数信息则会在出现在日志中。
感谢大家的浏览,本文为博主原创,未经允许不可转载。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)