[Funkunux] Linux_2.6.22.6 内核start_kernel函数分析之parse_args

在我的上一篇文章“ [Funkunux] Linux_2.6.22.6的Makefile分析 ”中,已经找到linux内核的第一条代码的位置是head.s,在head.s中,内核将bootloader中传给内核的参数进行解析,比对机器ID等参数,设置页表,开启MMU,然后跳转到/init/Main.c中的start_kernel()中进行一系列初始化。

以下是start_kernel函数的分析,重点分析命令行参数的解析函数parse_args:


博主使用的开发板是S3C2440,BOOTLOADER是U-BOOT,传入的命令行参数是#bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0#


我们可以在start_kernel中找到这一句:
parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption);
在parse_args中调用了:
parse_one(static_command_line, val, __start___param, __stop___param - __start___param, &unknown_bootoption);
在parse_args中调用了:
handle_unknown(param, val); //也就是parse_args中传入的函数指针unknown_bootoption


我们搜索unknown_bootoption,发现其调用了obsolete_checksetup函数:

static int __init obsolete_checksetup(char *line)
{
	struct obs_kernel_param *p;
	int had_early_param = 0;

	p = __setup_start;
	do {
		int n = strlen(p->str);
		if (!strncmp(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] == '\0' || line[n] == '=')
					had_early_param = 1;
			} else if (!p->setup_func) {
				printk(KERN_WARNING "Parameter %s is obsolete,"
				       " ignored\n", p->str);
				return 1;
			} else if (p->setup_func(line + n))
				return 1;
		}
		p++;
	} while (p < __setup_end);

	return had_early_param;
}
我们注意到了一个结构体struct obs_kernel_param:

struct obs_kernel_param {
	const char *str;
	int (*setup_func)(char *);
	int early;
};
以及该结构体的两个指针地址: __setup_start 和 __setup_end:

我们可以在vmlinux.lds中发现这两个地址:

  __setup_start = .;
   *(.init.setup)
  __setup_end = .;

可知这两个地址中包含的是被设为.init.setup段的内容,搜索.init.setup可以找到两个宏:

#define __setup(str, fn)					\
	__setup_param(str, fn, fn, 0)
    
#define __setup_param(str, unique_id, fn, early)			\
	static char __setup_str_##unique_id[] __initdata = str;	\
	static struct obs_kernel_param __setup_##unique_id	\
		__attribute_used__				\
		__attribute__((__section__(".init.setup")))	\
		__attribute__((aligned((sizeof(long)))))	\
		= { __setup_str_##unique_id, fn, early }
并且可以找到:
static int __init root_dev_setup(char *line)
{
	strlcpy(saved_root_name, line, sizeof(saved_root_name));
	return 1;
}

__setup("root=", root_dev_setup);
static int __init init_setup(char *str)
{
	unsigned int i;

	execute_command = str;
	/*
	 * In case LILO is going to boot us with default command line,
	 * it prepends "auto" before the whole cmdline which makes
	 * the shell think it should execute a script with such name.
	 * So we ignore all arguments entered _before_ init=... [MJ]
	 */
	for (i = 1; i < MAX_INIT_ARGS; i++)
		argv_init[i] = NULL;
	return 1;
}
__setup("init=", init_setup);
static int __init console_setup(char *str)
{
	char name[sizeof(console_cmdline[0].name)];
	char *s, *options;
	int idx;

	/*
	 * Decode str into name, index, options.
	 */
	if (str[0] >= '0' && str[0] <= '9') {
		strcpy(name, "ttyS");
		strncpy(name + 4, str, sizeof(name) - 5);
	} else {
		strncpy(name, str, sizeof(name) - 1);
	}
	name[sizeof(name) - 1] = 0;
	if ((options = strchr(str, ',')) != NULL)
		*(options++) = 0;
#ifdef __sparc__
	if (!strcmp(str, "ttya"))
		strcpy(name, "ttyS0");
	if (!strcmp(str, "ttyb"))
		strcpy(name, "ttyS1");
#endif
	for (s = name; *s; s++)
		if ((*s >= '0' && *s <= '9') || *s == ',')
			break;
	idx = simple_strtoul(s, NULL, 10);
	*s = 0;

	add_preferred_console(name, idx, options);
	return 1;
}
__setup("console=", console_setup);

显然,通过__setup(name,fn);这个宏,定义了三个结构体变量,并链接进 .init.setup 段中,early均为0
而 obsolete_checksetup 这个函数的工作就是遍历 .init.setup 段,根据bootloader传入的命令行参数,搜索结构体 obs_kernel_param 中 str 的变量符合"root="、"init="、"console="等字符串的,调用对应的 setup_func 函数。至于如何解析命令行参数,是在 parse_args 中做的工作,大家可以自行分析。

按照调用关系,可整理出如下内容:

start_kernel()        
    ->  parse_args("Booting kernel", static_command_line, __start___param,__stop___param - __start___param,&unknown_bootoption);
        ->  parse_one(static_command_line, val, __start___param, __stop___param - __start___param, &unknown_bootoption);
            ->  if (handle_unknown) 
                {
                    DEBUGP("Unknown argument: calling %p\n", handle_unknown);
                    return handle_unknown(param, val);
                        ->  unknown_bootoption(param, val);
                            ->  obsolete_checksetup(param);
                                ->  root_dev_setup(val) //val="/dev/mtdblock3"
                                    {
                                        strlcpy(saved_root_name, val, sizeof(saved_root_name));
                                        return 1;
                                    }    
                                    
                                ->  console_setup(val) //val="ttySAC0"
                                    ->  add_preferred_console(char *name, int idx, char *options) //name="ttySAC"; idx=0; options=0;
                                        ->  console_cmdline[0].name= name;
                                            console_cmdline[0].options= options;
                                            console_cmdline[0].idx= idx;
                                            
                                ->  init_setup(val) //val="/linuxrc"
                                    -> execute_command = val;
                                        for (i = 1; i < 32; i++) //static char * argv_init[32+2] = { "init", NULL, };
                                            argv_init[i] = NULL;
                }
显然,调用完parse_args后:

saved_root_name="/dev/mtdblock3";
console_cmdline[0].name= name;
console_cmdline[0].options= 0;
console_cmdline[0].idx= 0;
execute_command ="/linuxrc";

往后,start_kernel()会用到这些参数,至于如何使用,请看我下一篇文章。












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值