linux-2.6.22.6分析(三)启动流程之第二阶段

内核启动第二阶段的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进程

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值