Linux内核启动流程分析

1、

uImage一般是vmlinux和一些头部信息组成,其中vmlinux就是编译出来的文件

2、

分析内核的启动流程一般就是看Makefile文件,我们首先来分析内核目录的顶层Makefile文件:

 1 all: vmlinux  #文件的第一个目标
 2 #############################
 3 
 4 vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
 5 
 6 vmlinux-lds  := arch/$(ARCH)/kernel/vmlinux.lds
 7 
 8 #####   接下来就是vmlinux编译时候依赖的各种目标以及子目标   #####
 9 vmlinux-init := $(head-y) $(init-y)
10 
11 head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o
12 init-y    := init/
13 
14 
15 vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
16 
17 drivers-y := drivers/ sound/
18 net-y    := net/
19 libs-y    := lib/
20 core-y    := usr/
21 
22 ###   上面的目标就替换为最终编译出来的目标文件built-in.o  ######
23 
24 init-y := $(patsubst %/, %/built-in.o, $(init-y))
25 core-y    := $(patsubst %/, %/built-in.o, $(core-y))
26 drivers-y    := $(patsubst %/, %/built-in.o, $(drivers-y))
27 net-y    := $(patsubst %/, %/built-in.o, $(net-y))
28 libs-y1    := $(patsubst %/, %/lib.a, $(libs-y))
29 libs-y2    := $(patsubst %/, %/built-in.o, $(libs-y))
30 libs-y    := $(libs-y1) $(libs-y2)

3、

编译的时候我们其实可以看到打印很多编译需要用到的文件,如链接脚本,输出的.o文件以及用到的编译代码原材料:

比如编译脚本:rch/$(ARCH)/kernel/vmlinux.lds

第一个文件是arch/arm/kernel/head.S,然后就是按照编译时排列的顺序的文件和目录

 

4、

内核启动流程:

1)
arch/arm/kernel/head.S:
    ldr    r13, __switch_data        @ address to jump to after
    
arch/arm/kernel/head-common.S:
    __switch_data:
        .long    __mmap_switched
    
    __mmap_switched:
        b    start_kernel  @跳转到start_kernel函数,不再返回

(2)在init/main.c文件里面
start_kernel:
    setup_arch(&command_line);
    setup_command_line(command_line);
    parse_early_param:
        do_early_param:
            for (p = __setup_start; p < __setup_end; p++){
            /*
            调用从__setup_start到__setup_end之间的函数,
            其中,在arch/arm/kernel/vmlinux.lds.S中有相关的定义,表示所有文件的.init.setup的代码段
            __setup_start = .;
            *(.init.setup)
            __setup_end = .;
            */
            }
    rest_init:
        kernel_init:
            prepare_namespace:
                mount_root;   //挂载根文件系统
            init_post;

(3)
第2步的.init.setup的代码段又是怎么来的呢?
其实也就是从一个宏定义来的:

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

__setup("root=", root_dev_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 }

由上面可以看出来,每个用__setup宏定义调用的函数都会用.init.setup表示,在编译的时候就放在__setup_start到__setup_end之间

 

posted @ 2017-11-12 17:31  伊斯科明  阅读(297)  评论(0编辑  收藏  举报