linux系统启动流程可以简单总结为以下几步
1)开机BIOS自检
2)读取MBR,进行MBR引导
3)启动bootloader
4)加载内核kernel
5)启动init进程,依据inittab文件设定运行级别
6)init进程,执行rc.sysinit文件
7)启动内核模块,执行不同级别的脚本程序
8)执行/etc/rc.d/rc.local
9)启动mingetty,进入系统登陆界面
CPU上电后,它的PC寄存器指针指向IC内嵌的一片ROM的起始位置处,这片ROM称之为BROM(boot rom),系统就是通过这片BROM引导起来的。BROM的大小一般是32/64KB,IC上的ShareRAM大小也不尽相同。BROM中会存储上电引导程序,这段程序也一般会包括以下几个内容:
1. CPU上电初始化操作。
2. 启动介质的驱动操作。
3. 固件下载操作,用来进入刷机模式,更新固件使用。
4. BROM引导程序,主要功能是用来从从启动介质中读取和加载第二阶段引导程序。
5. 签名验证操作。
BROM中的引导程序由IC厂商自己定制开发。它会根据硬件上不同,来判断要进入刷机模式还是启动模式。下来要从启动介质中读取MBREC上的引导程序到ShareRAM中,并跳转执行。
2、读取MBR,进行MBR引导
Bios自检完成后会执行一段程序,目的在于读取硬盘的0柱面、0磁头的1扇区的主引导记录(MBR),bios找到MBR后会把它拷贝到提前指定好的物理地址上执行。
3、启动bootloader
BootLoader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核做好一切准备;具体所做内容可分为如下stage1和stage2两部分:
Stage1:(汇编编写)
· 前面总结过的部分,初始化异常向量表,设置工作模式,关中断
·配置cp15,初始化mmu cache tlb
·板级初始化,pll memory初始化
Stage2:(汇编->C编写)
·初始化本阶段要使用到的硬件设备(主要是外设)
·将内核映像和根文件系统映像从 flash 上读到RAM 中
·调用内核
这块流程中stage1所描述的内容对应我们S32V平台代码就是/u-boot/arch/arm/cpu/armv8/start.S,入口函数链接在/u-boot/arch/arm/cpu/armv8/s32/u-boot.lds;stage2阶段代码主要在crt0.S中,通过这个文件调用相应的C文件进行串口等外围基本设备的初始化。
4、加载内核
Uboot启动内核的过程是通过读取环境变量env中的bootcmd来决定如何启动kernel, 启动kernel的关键是bootm命令;而bootm命令的实现在uboot中是do_bootm()函数中(cmd_bootm.c)。经过uboot引导以后,系统开始进入到zImage中执行。zImage是包括了解压缩代码和vmlinux的镜像,所以它的执行可以分为三部分,分别是zImage解压缩,vmlinux内核启动汇编阶段,vmlinux内核启动C语言阶段。
(1)zImage解压缩
(2) 内核启动汇编阶段
(3) 内核启动c语言阶段(start_kernel到创建第一个进程)
这一阶段所涉及的文件也只有两个:
(1)arch/arm64/kernel/vmlinux.lds
(2)arch/arm64/kernel/head.S
内核启动汇编阶段
启动汇编阶段的代码是从arch/arm/kernel/head.S开始的,执行起点是stext函数。入口函数是通过vmlinux.lds中的ENTRY(stext)指定的。需要区分的是,在汇编.S文件中也有ENTRY的宏定义,它需要和ENDPROC成对出现,表示定义的一个函数。另外在.S文件中也要指明当前代码所在的段,比如:
__HEAD
ENTRY(stext)
……
ENDPROC(stext)
__HEAD是声明为.head.text段的宏定义,ENTRY和ENDPROC用来定义一个stext的函数。相对应的lds文件如下所示,其中的_text是lds中定义的常量,不同的段中会存在不同的相关常量,如.head.text段的_text常量,.text段的_stext和_etext常量:
.head.text : {
_text = .;
*(.head.text)
}
这部分主要完成的工作有cpu ID检查,machine ID检查,创建初始化页表,设置C代码运行环境,跳转到内核第一个真正的C函数start_kernel开始执行。
5、启动init进程,依据inittab文件设定运行级别
内核被加载后,第一个运行的程序便是/sbin/init,该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作。其实/etc/inittab文件最主要的作用就是设定Linux的运行等级,其设定形式是“:id:5:initdefault:”,这就表明Linux需要运行在等级5上。Linux的运行等级设定如下:
0:关机
1:单用户模式
2:无网络支持的多用户模式
3:有网络支持的多用户模式
4:保留,未使用
5:有网络支持有X-Window支持的多用户模式
6:重新引导系统,即重启
6、init进程,执行rc.sysinit文件
在设定了运行等级后,Linux系统执行的第一个用户层文件就是/etc/rc.d/rc.sysinit脚本程序,它做的工作比较多,包括设定PATH、设定网络配置(/etc/sysconfig/network)、启动swap分区、设定/proc等等,具体如下:
(1)获取网络环境与主机类型。首先会读取网络环境设置文件"/etc/sysconfig/network",获取主机名称与默认网关等网络环境。
(2)测试与载入内存设备/proc及usb设备/sys。除了/proc外,系统会主动检测是否有usb设备,并主动加载usb驱动,尝试载入usb文件系统。
(3)决定是否启动SELinux。
(4)接口设备的检测与即插即用(pnp)参数的测试。
(5)用户自定义模块的加载。用户可以再"/etc/sysconfig/modules/*.modules"加入自定义的模块,此时会加载到系统中。
(6)加载核心的相关设置。按"/etc/sysctl.conf"这个文件的设置值配置功能。
(7)设置系统时间(clock)。
(8)设置终端的控制台的字形。
(9)设置raid及LVM等硬盘功能。
(10)以 方式查看检验磁盘文件系统。
(11)进行磁盘配额quota的转换。
(12)重新以读取模式载入系统磁盘。
(13)清除启动过程中的临时文件。
(14)将启动信息加载到"/var/log/dmesg"文件中。
7、启动内核模块,执行不同级别的脚本程序。
当/etc/rc.d/rc.sysinit执行完后,系统就可以顺利工作了,只是还需要启动系统所需要的各种服务,这样主机才可以提供相关的网络和主机功能,因此便会执行/etc/rc脚本,该脚本文件定义了服务启动的顺序是先K后S,S是表示的是启动时需要start的服务内容,K表示关机时需要关闭的服务内容而具体的每个运行级别的服务状态是放在/etc/rc*.d(*=0~6)目录下,所有的文件均是指向/etc/init.d下相应文件的符号链接。rc.sysinit通过分析/etc/inittab文件来确定系统的启动级别,然后才去执行/etc/rc*.d下的文件。
8、执行/etc/rc.local
这部分不做多的介绍,如果打开了此文件,里面有如下一句话:
This script will be executed *after* all the other init scripts.You can put your own initialization stuff in here if you don't want to do the full Sys V style init stuff.
就是在一切初始化工作后,Linux留给用户进行个性化的地方。你可以把你想设置和启动的东西放到这里。
9、启动mingetty,进入系统登陆界面
这块主要是运行/bin/login脚本进行页面登录。