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

来源:互联网 发布:淘宝里怎么打开链接 编辑:程序博客网 时间:2024/06/16 01:51

在我的上一篇文章“ [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");#endiffor (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()会用到这些参数,至于如何使用,请看我下一篇文章。












0 0