linux2.3.22.6内核启动第二阶段(start_kernel函数主要流程)(2)
- UID
- 1029342
- 性别
- 男
|
linux2.3.22.6内核启动第二阶段(start_kernel函数主要流程)(2)
进入parse_tags(tags);函数:
static void __init parse_tags(const struct tag *t)
{
for (; t->hdr.size; t = tag_next(t))
if (!parse_tag(t))
printk(KERN_WARNING
"Ignoring unrecognised tag 0x%08x\n",
t->hdr.tag);
}
对tag数组中的每一个tag依次调用parse_tag(t)函数进行解析,进入parse_tag(t)函数:
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
parse_tag(t)函数就是在段(此段在连接文件中规划)
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .;中依次比较,看看与段中的那个标记相同,如果找到就去调用标记对应的函数。
在内核中搜索“*(.taglist.init)”找到
#define __tag __used __attribute__((__section__(".taglist.init")))
#define __tagtable(tag, fn) \
static struct tagtable __tagtable_##fn __tag = { tag, fn }
在内核中搜索__tagtable找到
__tagtable(ATAG_MEM, parse_tag_mem32);
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
。。。。。。。。。。。。
等定义,我们组要关心列出的这两个,ATAG_MEM与ATAG_CMDLINE这两个不正是UBOOT中定义的两个标记么。
通过以上分析其实解析参数的过程就是这个样子的:通过宏__tagtable()将各种标记及处理该标记的函数对应起来,通过附加强制属性将其放到段
__tagtable_begin = .;
*(.taglist.init)
__tagtable_end = .
中,解析函数时就是通过标记好在该段中查找,如若找到标记号,就通过该标记号对应的函数做进一步处理。
ATAG_MEM,对应的函数为:parse_tag_mem32,该函数实现功能就是:根据内存tag定义的内存起始地址、长度。在全局结构变量meminfo中增加内存的描述信息,以后内核就可以通过meminfo结构了解开发板的内存信息。
ATAG_CMDLINE对应的函数为parse_tag_cmdline这个函数实现的功能主要是:简单的将命令行tag的内容复制到字符串default_command_line(set_arch函数开头定义了这个变量)中。
接着往下分析
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
// 这就就是对init_mm结构体进行赋值,具体不了解这些东西咋用的,但是就是将text和data段给赋值了
memcpy(boot_command_line, from, COMMAND_LINE_SIZE);//将命令行信息复制给变量boot_command_line
boot_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from);
//扫描命令行参数,对其中的一些参数做先期处理,这些参数用__early_param定义,
__early_param("mem=", early_mem);
__early_param("initrd=", early_initrd);
放到了
段:
__early_begin = .;
*(.early_param.init)
__early_end = .; ( 脚本文件中规划好的)
中。(注意是命令行内部的参数处理,例如命令行参数中若含有“men=”字符串就会调用函数early_mem)
paging_init(&meminfo, mdesc);//根据前面解析内存tag(arse_tag_mem32,该函数在全局结构变量meminfo中增加内存的描述信息)及setup_machine函数返回的mdesc。
request_standard_resources(&meminfo, mdesc);
好了,到此为止下面的就不分析了,对我们理解参数解析作用不大了,现在来总结一下
内核解析参数,总的来说分两块,第一解析除命令行意外的标记,如内存标记等,命令行参数标记是单独拿出来解析的,而且还进行了先期处理。
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
cpu_init();
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
#if defined(CONFIG_KGDB)
extern void __init early_trap_init(void);
early_trap_init();
#endif
}
回到函数start_kernel中接着往下看,下面还有两个与参数解析相关的函数。
函数:setup_command_line(command_line);
里面将boot_command_line内容复制给了saved_command_line
将command_line内容复制给了static_command_line
接着在函数start_kernel内往下看:
函数parse_early_param();
函数parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
函数parse_args调用函数unknown_bootoption对命令行参数进行一一解析。怎么解析呢,
跟进去,进入函数unknown_bootoption,调用了函数obsolete_checksetup,进入函数obsolete_checksetup,函数obsolete_checksetup实现的功能就是,将命令行中的参数在段
__setup_start = .;
*(.init.setup)
__setup_end = .;(在连接文件中已规划好)
中进行一一比对,若找到了就调用参数所指定的函数进行处理。具体实现见如下分析:
在内核中搜索.init.setup找到了:
#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 }
接着再内核中搜索“ __setup_param”找到了:如下的宏:
#define __setup_null_param(str, unique_id) \
__setup_param(str, unique_id, NULL, 0)
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
#define early_param(str, fn) \
__setup_param(str, fn, fn, 1)
继续跟进搜索“__setup”你会找到诸如一下这样的一系列的宏定义:
__setup("root=", root_dev_setup);
__setup("rootflags=", root_data_setup);
__setup("rootfstype=", fs_names_setup);
__setup("rootdelay=", root_delay_setup);
__setup("console=", console_setup);绿色部分不正是我们命令行中的字符串么!!!!!
执行完函数parse_args后,命令行参数就解析完了,至此UBOOT传递过来的所有参数也就都解析完毕了。
回到start_kernel函数中
找到函数console_init();这也是个比较重要的函数,具体分析见韦东山老师编写的《嵌入式Linux应用开发完全手册》:P325页。
start_kernel函数最后调用到了rest_init();函数,rest_init();函数又调用到了函数kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);相当于执行函数:kernel_init,函数kernel_init又调用到了函数prepare_namespace();,函数prepare_namespace();最终调用到了mount_root();挂载根文件系统,挂载成功后返回到函数prepare_namespace()中,又调用到了init_post函数,在函数init_post中调用
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
开始执行应用程序。 |
|
|
|
|
|