Linux内核参数


本人刚开始学linux内核,有理解错误地方欢迎指正!

内核版本2.6.32.2

/*
 *在include/linux/autoconf.h中定义,也即是在通过make menuconfig中进行手动配置产生的
*/
#define CONFIG_CMDLINE "console=ttySAC0,115200" 

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

void __init setup_arch(char **cmdline_p)
{
	...
	...
	...
	char *from = default_command_line;

	...
	...
	...

	/*
	 *将命令行参数(CONFIG_CMDLINE中的值["console=ttySAC0,115200"])复制到全局变量boot_command_line中
	*/
	memcpy(boot_command_line, from, COMMAND_LINE_SIZE);
	boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
	parse_cmdline(cmdline_p, from);
	
	...
	...
	...
}

struct early_params {
	const char *arg;
	void (*fn)(char **p);
};

/*
 *该宏定义一个struct early_params结构的变量,并将该变量的数据放在".early_param.init"段中
*/
#define __early_param(name,fn)					\
static struct early_params __early_##fn  __used	 __attribute__((__section__(".early_param.init"))) = { name, fn }

/*
 *在arch/arm/kernel/vmlinux.lds.S文件中有如下定义
*/
__early_begin = .;
	*(.early_param.init)
__early_end = .;

/*
 *上面两段内容实际上表示的意思是:
 *将所有通过宏(__early_param)定义的变量都放在".early_param.init"段中,
 *可以将".early_param.init"段理解成一个数组,所有的变量按照数组的排
 *列方式组合在一起,__early_begin就是这个数组的起始地址,__early_end
 *就是这个数组的结束地址.
*/

static char __initdata command_line[COMMAND_LINE_SIZE];

static void __init parse_cmdline(char **cmdline_p, char *from)
{
	char c = ' ', *to = command_line;//command_line为空数组
	int len = 0;

	for (;;) {
		if (c == ' ') {
			extern struct early_params __early_begin, __early_end;
			struct early_params *p;

			/*
			 *通过上面的解释,这里可以理解为遍历数组中的所有元素
			*/
			for (p = &__early_begin; p < &__early_end; p++) {
				int arglen = strlen(p->arg);
				
				/*通过比较名字来判断是否找到能解析该命令行参数的数据结构*/
				if (memcmp(from, p->arg, arglen) == 0) {
					if (to != command_line)
						to -= 1;
					from += arglen;
					/*调用定义的函数来解析该命令行参数*/
					p->fn(&from);
					
					/*查找下一个命令行参数*/
					while (*from != ' ' && *from != '\0')
						from++;
					break;
				}
			}
		}
		
		/*下面6行代码表示将没有找到的其它命令行参数复制到全局变量command_line中*/
		c = *from++;
		if (!c)
			break;
		if (COMMAND_LINE_SIZE <= ++len)
			break;
		*to++ = c;
	}
	*to = '\0';
	*cmdline_p = command_line;
}
/*
 *通过上面的理解可以得出parse_cmdline函数的作用:
 *如果某个命令行参数已经通过宏__early_param定义了,那么就调用它的解析函数
 *来解析它;如果没有定义,那么就把它复制到全局变量command_line中.
 *假如命令行参数为"console=ttySAC0,115200 mem=16M",那么:
 * mem=16M将被解析出来,而"console=ttySAC0,115200"则被复制到全局变量command_line中
*/

char *saved_command_line;
static char *static_command_line;

static void __init setup_command_line(char *command_line)
{
	saved_command_line = alloc_bootmem(strlen (boot_command_line)+1);
	static_command_line = alloc_bootmem(strlen (command_line)+1);
	strcpy (saved_command_line, boot_command_line);
	strcpy (static_command_line, command_line);
}
/*
 *setup_command_line函数很简单,作用:
 *1,为全局变量saved_command_line和static_command_line分配内存;
 *2,将全局变量boot_command_line的内容复制到saved_command_line中
 *3,将    变量command_line的内容复制到static_command_line中
 *说明:boot_command_line中的内容为没有被解析过的命令行参数
 *      command_line中的内容为已经被解析过的命令行参数
*/


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;
}

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

int parse_args(const char *name,
	       char *args,
	       struct kernel_param *params,
	       unsigned num,
	       int (*unknown)(char *param, char *val))
{
	char *param, *val;

	...

	/* 跳过起始空格部分 */
	while (isspace(*args))
		args++;

	while (*args) {
		int ret;
		
		...
		
		/*解析命令行参数部分,函数完成后,param=参数名,val=该参数的值,并返回指向下一个
		 *参数地址的指针
		 */
		args = next_arg(args, ¶m, &val);
		...
		
		ret = parse_one(param, val, params, num, unknown);
		
		...
		...
		...
	}

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

/*quote 引号("")*/
static char *next_arg(char *args, char **param, char **val)
{
	unsigned int i, equals = 0;
	int in_quote = 0, quoted = 0;
	char *next;

	/*
	 *如果以引号(")开头
	 */
	if (*args == '"') {
		/*跳过引号*/
		args++;
		/*标记在引号中*/
		in_quote = 1;
		/*标记被引用*/
		quoted = 1;
	}

	for (i = 0; args[i]; i++) {
		/*
	   *如果遇到空格并且已经跳出引号之外[或者从来没有出现过引号]
	   *那么跳出for循环,表示已经找到一个完整的arg
	   *比如[console=ttySAC0,115200]
	   */
		if (isspace(args[i]) && !in_quote)
			break;
			
		/*
	   *如果从来没有出现过等号(=)
	   *那么等号出现时,记下该等号的下标(i)
	   *比如[console=ttySAC0,115200]中,equals = i = 7
	   */
		if (equals == 0) {
			if (args[i] == '=')
				equals = i;
		}
		
		/*如果再次出引号,将in_quote的值取反*/
		if (args[i] == '"')
			in_quote = !in_quote;
	}

	*param = args;
	
	/*如果没有检测到等号(=),那么表示该参数的值为空;否则不为空*/
	if (!equals)
		*val = NULL;
	else {
		/*将等号(=)位置的字符重新赋值为NULL,例如此时 *param = console */
		args[equals] = '\0';
		
		/*  *val指向等号(=)下一个字符的地址 */
		*val = args + equals + 1;

		/* Don't include quotes in value. */
		/*不要将引号包含在值里面*/
		if (**val == '"') {
			(*val)++;
			if (args[i-1] == '"')
				args[i-1] = '\0';
		}
		if (quoted && args[i-1] == '"')
			args[i-1] = '\0';
	}

	if (args[i]) {
		args[i] = '\0';
		next = args + i + 1;
	} else
		next = args + i;

	/* Chew up trailing spaces. */
	while (isspace(*next))
		next++;
	return next;
}

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

	/* Find parameter */
	/*通过上面传过来的参数值来看,num_params = 0,所以这里什么都不做*/
	for (i = 0; i < num_params; i++) {
		if (parameq(param, params[i].name)) {
			DEBUGP("They are equal!  Calling %p\n",
			       params[i].set);
			return params[i].set(val, ¶ms[i]);
		}
	}

	/*这里的handle_unknown = do_early_param,所以实际上是在调用do_early_param函数*/
	if (handle_unknown) {
		DEBUGP("Unknown argument: calling %p\n", handle_unknown);
		return handle_unknown(param, val);
	}

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



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

#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 }
/*
 *该宏定义了两个变量:
 *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 }
 */
 
#define __setup(str, fn)					\
	__setup_param(str, fn, fn, 0)

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

/*
 *比如  __setup("console=", console_setup) 这个宏定义了如下两个变量
 *static const char __setup_str_console_setup[] __initconst __aligned(1) = "console=";
 *static struct obs_kernel_param __setup_console_setup  __used __section(.init.setup) __attribute__((aligned((sizeof(long)))))
 *       = { __setup_str_console_setup, console_setup, 0 }
 */

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

	for (p = __setup_start; p < __setup_end; p++) {
		/*如果p->early = 1 并且参数名相等*/
		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;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值