1、内核配置编译过程

/Linux-4.9.88$ make mrproper
/Linux-4.9.88$ make 100ask_imx6ull_defconfig
/Linux-4.9.88$ make zImage

2、make 100ask_imx6ull_defconfig的作用

分析过内核的启动过程后,我逐渐意识到make 100ask_imx6ull_defconfig命令的重要性,我们vi去看看里面包含了什么重要的东西。

CONFIG_ARCH_MXC=y
CONFIG_SOC_IMX6ULL=y

  我们看看这两项有什么作用。

arch/arm/Makefile中决定了会编译其中的哪一些单板,

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

当我们vi arch/arm/Makefile中会看到

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 所有,Makefile会编译到arch/arm/mach-imx这个目录中的文件。这样就将我们编译到的单板定位到了imx目录下

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 可以看到里面确实多了很多.o文件,说明被编译了。

那么,虽然编译了mach-imx下的文件,但是imx文件中还有很多的单板,又怎么确定我们编译的是哪一个单板呢?

接下来,我们看看CONFIG_SOC_IMX6ULL=y的作用,因为涉及到了某一个固定的单板,这样跟设备树会有很大的关系了。因此我们去设备树目录下看看,能发现什么。

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 看到这么多关于imx6ull的设备树,我们首先想到的就是Makefile

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 这样,我们就看到了,只要是定义了CONFIG_SOC_IMX6ULL=y之后,我们想使用的设备树就会被编译到,我们使用的设备树是100ask_imx6ull-14×14.dts。

我们看看里面包含了什么。

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 我们看到,在根目录下包含了一个“compatible”属性,他就说明了,与他匹配的文件中也肯定会有相同的“compatible”属性。

搜索!!目标越来越近了

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 就是他了

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 我们看到,这个文件中确实包含了imx6ull相关的单板信息,当我们把DT_MACHINE_START展开,就会看到,他是包含在.arch.info.init段中的。以后会提到他的用途。

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 3、内核启动过程

下面我们顺着第一句程序往下走,我们看arch/arm/kernel/vmlinux.lds中的程序入口是哪

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 

 他是定义在arch/arm/kernel/head.S中的

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 咦,我们会发现,竟然没有低内核版本中设置机器ID的操作了。

作者是这么说的:

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 前面经过一系列程序后,就是为了

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

 

 最后在__mmap_switched中,会调用到

 开始了启动内核的道路

其中有两个函数

基于imx6ull的Linux-4.9.88内核启动过程-风君雪科技博客

setup_arch
    setup_machine_fdt
        mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
            of_flat_dt_match
                of_fdt_match
                    of_fdt_is_compatible
                        cp = fdt_getprop(blob, node, "compatible", &cplen);
                        if(of_compat_cmp(cp, compat, strlen(compat)) == 0)
     dump_machine_table
      for_each_machine_desc(p)
        early_print("%08x %s ", p->nr, p->name);
#define for_each_machine_desc(p)
  for (p = __arch_info_begin; p < __arch_info_end; p++) 

  看到了吧!结合上一节的内容,这样就把imx6ull单板的信息接收到了!

setup_arch
    parse_early_param
        parse_early_options
            do_early_param
                for (p = __setup_start; p < __setup_end; p++) /* 开始处理__setup段early定义的内容了 */

unknown_bootoption
    obsolete_checksetup
        p = __setup_start;/* 开始处理__setup段非early定义的内容了 */
	do {
		int n = strlen(p->str);
		if (parameqn(line, p->str, n)) {
			if (p->early) {
				if (line[n] == '' || line[n] == '=')
					had_early_param = true;
			} else if (!p->setup_func) {
				pr_warn("Parameter %s is obsolete, ignored
",
					p->str);
				return true;
			} else if (p->setup_func(line + n))
				return true;
		}
		p++;
	} while (p < __setup_end);

  

mm_init();
sched_init();
init_IRQ();
hrtimers_init();
softirq_init();
console_init();
rest_init();    /* 这是一个关键的函数 */

  

rest_init()
    kernel_thread(kernel_init, NULL, CLONE_FS);/* 创建第一个进程 */

kernel_init
    kernel_init_freeable
        if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
		pr_err("Warning: unable to open an initial console.
");

	(void) sys_dup(0);
	(void) sys_dup(0);    /* 这就是熟悉的标准输入 标准输出 标准错误 */
        
        /* 别忘了内核的目的是为了挂载根文件系统 */  
        prepare_namespace
            saved_root_name中保存的就是挂载根目录的位置
                mount_root();  /* 挂载根文件系统 */

static int __init root_dev_setup(char *line)
{
	strlcpy(saved_root_name, line, sizeof(saved_root_name));
	return 1;
}

__setup("root=", root_dev_setup);    /* 看到了吧 */             

  

kernel_init
    kernel_init_freeable    /* 上面已经分析过了 */
    /* 接下来就是运行init进程 */
    if(execute_command)
        run_init_process(execute_command);

    /* 执行到下面的任意一个就不会返回了 */
    try_to_run_init_process("/sbin/init")
    try_to_run_init_process("/etc/init")
    try_to_run_init_process("/bin/init")
    try_to_run_init_process("/bin/sh")

static int __init init_setup(char *str)
{
	unsigned int i;

	execute_command = str;    /* execute_command进程也是从__setup传入的 */
	/*
	 * 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);