内核启动第二阶段的start_kernel函数位于init/main.c
1、处理u-boot传入的启动参数(含命令行参数)
命令行参数会被提取出来,保存到一个个结构体中,这在文末提及
setup_arch(&command_line);
setup_command_line(command_line);
2、挂接根文件系统并调用应用程序(逐级缩进表示逐级调用)
rest_init
kernel_init(通过创建线程调用)
prepare_namespace
mount_root/*挂接根文件系统*/
init_post
sys_open((const char __user *) "/dev/console", O_RDWR, 0)/*打开控制台*/
run_init_process/*调用应用程序*/
挂接根文件系统需要明确挂接在flash的哪个分区,分区表的定义在:arch/arm/plat-s3c24xx/common-smdk.c
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND,
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
prepare_namespace中确定了挂接分区ROOT_DEV
if (saved_root_name[0]) {
root_device_name = saved_root_name;
if (!strncmp(root_device_name, "mtd", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name);
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
ROOT_DEV是由root_device_name即saved_root_name转存而来
搜索发现saved_root_name在obsolete_checksetup中被设置,这个函数会对__setup_start到__setup_end中,成员early=0的结构体,调用它的函数成员,并且将分区名称保存在saved_root_name
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);
__setup_start、__setup_end引用自链接脚本,类型为obs_kernel_param,类型的定义在include/linux/init.h:
#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 }
搜索__setup可以在init/do_mounts.c找到如下定义
__setup("root=", root_dev_setup);
展开后是一个字符串和一个结构体:
static char __setup_str_root_dev_setup[] __initdata = "root=";
static struct obs_kernel_param __setup_root_dev_setup
__attribute_used__
__attribute__((__section__(".init.setup")))
__attribute__((aligned((sizeof(long)))))
= { __setup_str_root_dev_setup, root_dev_setup, early };
这个结构体的功能函数为root_dev_setup:
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));
return 1;
}
命令行参数中有root=mtdx来指定根文件挂机的分区,在前面处理命令行参数时,会提取出来,调用root_dev_setup,将挂接分区信息保存在saved_root_name中,最后转存ROOT_DEV,由此确定挂接分区
同理,init/main.c还有__setup("init=", init_setup);等定义,它们也是在前面处理命令行时被提取出来,在其它地方被调用,其中init用于指定系统运行的init进程