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之间