Linux内核启动流程
Linux内核启动流程
一、配置及编译
配置
三种方法配置:
-
直接输入 make menuconfig 命令,从头到尾重新配置菜单 (非常复杂)
make menuconfig时修改配置项,最终的配置结果会保存在.config文件中,这主要是Kconfig的功能,
再执行make menuconfig时就可以回去读取
.config
文件。这是内核配置的过程。 -
通过make xxx_defconfig命令在默认的配置上进行修改,然后再输入make menuconfig配置菜单
可以使用,使用
find –name \*defconfig
命令查找所有带defconfig名字的文件. -
使用厂家提供的配置config_ok文件
在linux-2.6.22.6目录下,使用
cp config_ok .config
将config_ok复制覆盖新的.config隐藏文件(通过 ls -la 命令可以查看.config隐藏文件),最后执行make menuconfig时就可以回去读取.config文件
可见,最后的配置文件为.config
文件。该配置文件通过make uImage
或make
命令后会影响以下几个文件:
-
相关的源代码(包含该宏,该宏的具体定义在下一个文件
autoconf.h
中) -
该源码所对应的头文件(include/linux/autoconf.h)
该头文件会通过make命令根据
.config
文件自动生成,同时将CONFIG_XXX定义为1(不论是否为模块) -
子目录中的makefile
编译成模块或者是内核是通过该文件体现出来的:
例子:
1
obj-$(CONFIG_XXX) += xxx.o
这里
CONFIG_XXX
的值就是下一点中的配置文件auto.conf
中的内容,即:- obj-y:编译到内核
- obj-m:编译成模块
-
配置文件(include/config/auto.conf)
该文件中的内容是通过make命令根据
.config
文件自动生成例子:
CONFIG_XXX=y(编译到内核)
或
CONFIG_XXX=m(编译成模块)
分析顶层makefile
分析makefile的主要目的是找到第一个文件和链接脚本,相关方法和uboot中的过程类似
详细的文档可以查看
Documentation/kbuild/makefiles.txt
通过上面的讲解,我们可以知道每个文件是如何被编译成模块或者是内核中去的,这里拓展下多个文件一起编译的情况:
例子:a.c、b.c两个文件需要同时编译成一个模块
1
2 obj-m += ab.o
ab-objs := a.o b.o此时会将a.c、b.c两个文件编译成ab.ko这个模块
通过make uImage
命令,发现目标uImage
位于arch/arm/makefile
:
顶层的makefile包括了子目录下的makefile以及上文中讲的
.config
配置文件
1 | ZImage Image xipImage bootpImage uImage:vmlinux |
此时目标vmlinux
又在顶层makefile中:
1 | all: vmlinux |
同分析uboot的方法一致,这里直接执行make uImage V=1
命令,关心最后一条命令(展开后):
V=1:打印信息更加详细的列出来
1 | arm-linux-ld -EL -P --no-urde fined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds |
可见:
- 第一个文件为:
arch/arn/kernel/head.S
- 链接脚本为:
arch/arm/kernel/vmlinux.lds
(根据make命令由同级目录下的vmlinux.lds.S文件生成)
1 | SECTIONS |
二、第一个文件head.S
head.S步骤:
-
检查是否支持该处理器
__lookup_processor_type
-
检查单板ID(就是uboot传进来的机器ID)
这里会通过
__lookup_machine_type
函数,将链接脚本中定义的.arch.info.init
段中存放的架构相关的初始化信息和uboot传进来的机器ID进行对比,如果不匹配就进入死循环注:通过MACHINE_START宏可以定义架构相关的初始化信息在
.arch.info.init
段中 -
创建页表
__create_page_tables
-
使能MMU
__enable_mmu
-
跳转到
start_kernel
函数(内核的第一个C函数)
三、启动内核start_kernel
start_kernel步骤
-
中断初始化、时钟初始化、打印内核版本信息
-
处理uboot传进来的启动信息
setup_arch(&command_line);
- 提取信息,处理
- 解析命令行信息
parse_cmdline
-
处理命令行信息
setup_command_line(command_line);
-
调度初始化等各种初始化
-
挂接根文件系统
rest_init
-
创建
kernel_init
线程-
早期的用户空间初始化
prepare_namespace
- 挂载根文件系统
mount_root
- 挂载根文件系统
-
初始化某些设备
init_post
-
打开/dev/console设备
-
执行应用程序
1
2
3
4run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
-
-
-
处理uboot传进来的启动信息
通过调用宏__setup
来处理uboot传进来的bootargs
命令行参数
1 | __setup("命令",调用的函数) |
例子:
__setup(“root=”,root_dev_setup):当uboot传进来
bootargs
命令行参数中有root参数时,会调用函数root_dev_setup。同时用了一个专门的结构体obs_kernel_param来保存相关的一些数据并存在了.init.setup
段中