Error[8]: Undefined offset: 934, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

1. __setup()介绍
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()函数进行解析。

2. 实现原理 2.1 __setup()宏展开

__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 之间

2.3 __setup(str, fn) fn何时被调用

让我们一步步从内核启动的开始进行跟踪:

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(&params[i]))
				err = params[i].ops->set(val, &params[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.strobs_kernel_param.setup_func()相等,则会调用dma_debug=off函数进行处理。

对于cmdline中包含obsolete_checksetup()内容时,param=“dma_debug=”,此时就会将"off"作为参数传递到dma_debug_cmdline()函数中进行解析。

3. 其他细节

上述以__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的话,传入的参数信息则会在出现在[+++]日志中。

感谢大家的浏览,本文为博主原创,未经允许不可转载。

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Linux中_软件运维_内存溢出

Linux中

Linux中,第1张

1. __setup()介绍
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()函数进行解析。

2. 实现原理 2.1 __setup()宏展开

__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 之间

2.3 __setup(str, fn) fn何时被调用

让我们一步步从内核启动的开始进行跟踪:

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(&params[i]))
				err = params[i].ops->set(val, &params[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.strobs_kernel_param.setup_func()相等,则会调用dma_debug=off函数进行处理。

对于cmdline中包含obsolete_checksetup()内容时,param=“dma_debug=”,此时就会将"off"作为参数传递到dma_debug_cmdline()函数中进行解析。

3. 其他细节

上述以__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的话,传入的参数信息则会在出现在日志中。

感谢大家的浏览,本文为博主原创,未经允许不可转载。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/926295.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存