linux启动流程
1. Linux启动流程
1.1 系统初始化(BIOS)
当我们摁下电源开关到屏幕开始输出信息(系统开机界面)之间这一步称为系统初始化。这一步主要用于检测各个硬件是否正常,不正常则发出凤鸣通知。正常则开始按照用户制定的顺序开始加载启动加载器(GRUB,Clover等)
1.1.1 (RESET)重置CPU
当我们按下电源开关时,电源就会开始向主板和其他设备供电,此时电压还不太稳定,主板芯片组会向CPU发送并保持一个RESET(重置)信号,让CPU内部自动恢复到初始状态,CPU此刻不会马上开始执行指令。当芯片组检测到电源已经开始稳定供电(当然从不稳定到稳定只有一瞬间),芯片组便撤去RESET信号,CPU马上就从地址FFFF0H处开始指令。这个地址一般是跳转到系统BIOS的地址范围的地址。
1.1.2 POST(Power-OnSelfTest)
系统BIOS被加载后首先要做的就是POST(Power-OnSelfTest,加电后自检),POST主要任务是检测系统中一些关键设备是否正常工作,例如内存和显卡等设备。由于POST是最早进行的检测过程,此时显卡还没有初始化,如果POST过程中出现致命错误,例如没有找到内存或内存有问题(一般只会检查640K常规内存),系统BIOS会直接控制喇叭发生来报告错误,声音长短和次数代表错误类型。在正常情况下POST过程非常快,几乎无法感知。POST结束后就会调用其他组件来进行更完整的硬件检测。
1.1.3 初始化设备(硬件)
系统BIOS将查找显卡的BIOS,存放显卡BIOS的ROM芯片的起始地址通常设在C0000H处,系统BIOS在这个地址上找到显卡BIOS后就调用其的初始化代码,由显卡BIOS来初始化显卡。此时多数显卡都会在屏幕上显示处一些初始化信息,生产厂商,图形芯片等内容。这个画面几乎一闪而过 。系统BIOS接着会查找其他设备的BIOS程序,找到之后同样调用这些设备BIOS内部的初始化代码来初始化相关设备。
1.1.4 BIOS信息输出
查找完所有其他设备的BIOS之后,系统BIOS将会显示出本身的启动画面,包括BIOS类型,序列号,版本号等内容
1.1.5 内存测试
系统BIOS将检测和显示CPU类型和工作频率,然后开始测试所有的RAM,并同时在屏幕上显示内存测试进度。可以才CMOS设置中自行设置使用耗时长还是耗时短的测试方法。
1.1.6 检测标准硬件设备
系统BIOS将开始检测系统中安装的一些标准硬件设备,包括硬盘,CD-ROM,串口,并口,软驱等设备。大多是新版BIOS在此时还会设置内存定时参数等一些高级参数。(超频玩家狂喜)
1.1.7 检测即插即用设备
系统BIOS内部的支持即插即用的代码将开始检测和配置系统中安装的即插即用设备,每找到一个设备之后,都会在屏幕上显示处设备的名称和型号等信息,同时为该设备分配中断,DMA通道和I/O端口等资源。
1.1.8 更新ESCD
系统BIOS将更新ESCD(Extended System Configuration Data ,扩展系统配置数据)。ESCD是系统BIOS用来与操作系统交换硬件配置的一种手段,这些数据被存放在CMOS(一小块特殊的RAM,由主板上的电池来供电)之中。通常ESCD数据只在系统硬件配置发生改变后才会更新。
1.1.9 启动引导器
ESCD更新完毕后,系统BIOS将会进行它最后一项工作,即根据用户指定的启动顺序从软盘,硬盘或光驱启动。系统BIOS将会读取并执行启动顺序中第一项的 MBR 1主引导记录(前446字节,总共512字节)。前446字节通常是启动加载器(boot loader)GRUB之类的程序,至此系统初始化完成。后续操作交由启动加载器(boot loader)完成
1.2 系统初始化(UEFI)
UEFI(Unified Extensible Firmware Interface Forum)的简称 ,是目前从智能手机到打印机,笔记本电脑,服务器,甚至超级计算都被广泛应用的技术标准,其中它与传统BIOS的不同之处可以用3句话进行概括:
1.规范了各种接口标准
2.为不同的操作系统提供统一的接口
3.代码开源
1.2.1 SEC阶段(安全验证)
SEC阶段接受和处理系统的启动,重启,异常信号。SEC会从CPU的缓存(一级缓存,二级缓存等)中开辟出一段空间作为内存使用(内存此时没有被初始化,后面程序运行需要内存和栈空间),这个过程叫做[Cache As RAM(CAR)“。最后将可启用的固件地址和大小还有内存和栈的地址和大小当作参数传递给下一阶段(PEI阶段)。
1.2.2 PEI阶段(EFI前期初始化)
在PEI阶段主要为DXE阶段准备执行环境,主要做CPU相关得硬件初始化,最主要得是对于内存得初始化,将DXE阶段所需要得参数以HOB列表的方式进行传递
1.2.3 DXE阶段(驱动执行环境)
在DXE阶段已经有足够得内存可以使用,因此可以完成大量得驱动加载和初始化工作。遍历所有固件中的Driver,当Driver所依赖得资源都满足要求时,调度Driver到队列执行,知道所有Driver都加载和执行完毕,系统完成初始化。
1.2.4 BDS阶段(启动设备选择)
在BDS阶段,主要初始化控制台设备,加载执行必要得设备驱动,根据用户得选择,执行相应得启动项。
1.2.5 TSL阶段(操作系统加载前期)
TSL阶段是OS Loader执行得第一个阶段,为OS Loader准备执行环境,OS loader调用ExitBootService结束启动服务,进入RunTime阶段
1.2.6 RT阶段
在RT阶段,OS loader已经完全取得系统控制权,因此要清理和回收一些之前被UEFI占用得资源,runtime services随着操作系统得运行提供相应得运行时服务,这个期间一旦出错和异常,将会进入AL进行修复
1.2.7 AL阶段(灾难恢复)
各厂家自定义修复方案,无官方标准
2. Boot loader (启动引导器)
启动加载器必须能够访问内核和 initramfs 映像,否则系统将无法引导。因此,在典型设置中,它必须支持访问 /boot。也就是说,启动加载器需要兼容内核和 initramfs 所处的位置的文件系统。参考GRUB引导得1.5步
启动引导器对比图2
2.1 加载并执行内核
启动引导器启动内核程序。内核是操作系统得核心。它运行于一个叫[内核空间]得底层上,负责机器硬件和应用程序之间得交流。为了尽可能充分地压榨CPU性能,内核使用调度器,通过一定优先级算法将CPU按照时间动态的分配给各个程序。
2.2 initramfs(早期用户空间)
在boot loader(启动引导器)加载kernel和可用的initramfs文件,并执行Kernel之后,Kernel将initramfs(初始RAM文件系统)压缩包解压缩到(然后清空)rootfs(初始根文件系统,特别是ramfs和tmpfs)。只读挂载 首先提取initramfs实在Kernel构建过程中嵌入kernel二进制Update translation的initramfs,然后提取可用的外部initramfs文件。因此,外部的initramfs文件会覆盖嵌入式initramfs中具有相同名称的文件。然后Kernel执行 /init (在rootfs)中作为第一个进程。(early userspace)3 开始。
initramfs中存放/init程序,执行/init之后开从临时文件系统加载和真正的根文件系统所需的设备驱动程序
早期使用的是initrd,后面就开始使用initramfs。
initramfs之所以存在,是为了帮系统访问真正的根文件系统。也就是说,硬件IDE,SCSI,SATA,USB/FW所要的Kernel模块,如果没有内置在Kernel中,就会被initramfs负责加载。initramfs只要有能够让系统访问真实跟文件系统的模块就可以了,不用尽可能地包含一切模块。当然,其它真正有用的模块之后在init流程中被udev加载好。
3. Init流程
在[早期用户空间]的最终环节里,真正的根文件系统被挂在后,就会替换掉原来的伪根文件系统。接着/sbin/init被执行,同样也替换掉原来/init的进程. 目前大多Linux系统选用的init程序是systemd。
本文讲解systemd,SysVinit网络上有很多自行查看
3.1 Systemd目标确定
首先,Systemd挂载/etc/fstab中的文件系统,包括内存交换分区。据此,systemd必须能够访问/etc目录下的配置文件,包括它自己。systemd借助其配置文件/etc/systemd/system/default.target决定Linux系统应该启动到那个状态(target)。default.target是一个符号链接,对于桌面系统,其连接到graphical.target,该文件相当于SysV init风格的runlevel5。 emergency.target相当于单用户模式。
SysV和systemd对应表
如上图Systemd启动的target和SysV init启动运行级别的对比。其中runlevel0.target类似这种的命名格式,是为了向前兼容SysV而提供的。
3.2 Systemd执行启动
每个target有一个在其配置文件中描述的依赖集,systemd需要首先启动其所需要依赖,这些依赖服务是Linux主机运行在特定的功能级别所要求的服务。当配置文件中所有的依赖服务都加载并运行之后,即说明系统运行于该目标级别。
图中箭头表示各单元(所有的服务和target均是systemd的单元)之间的依赖关系,先后顺序,图表按照自上而下的时间顺序.以×号开头的target是通用的启动状态,当达到其中的某一target,则说明系统已经启动完成。
systemd 也会查看老式的 systemV init 目录中是否存在相关启动文件,若存在,则 systemd 根据这些配置文件的内容启动对应的服务。
sysinit.target和basic.target 可以视作启动过程中的状态检查点。尽管systemd的设计初衷是并行启动系统服务,但是部分服务或功能target是其他服务或target启动的前提。系统将暂停于检查点知道其所要求的服务和target都满足为止。
sysinit.target 状态的到达是以其所依赖的所有资源模块都正常启动为前提的,所有其他的单元(unit),如果文件系统挂载,交换文件设置,设备管理器的启动,随机数生成器种子设置,低级别系统服务初始化,等都必须完成,但是在sysinit.target中这些服务与模块是可以并行启动的。
sysinit.target启动所有的低级别服务和系统初具功能所需的单元,这些都是进入下一个阶段basic.target的必要前提。
在sysinit.target的条件满足后,systemd接下来启动basic.target,启动其所要求的所有单元。basic.target通过启动下一target所需的单元提供了更多的功能,包括各种可执行文件的目录路径,通信scokets等
最后,用户级target(multi-user.target或graphical.target)可以初始化了,应该注意的是multi-user.target必须在满足图形化target,graphical.target的依赖项之前达成
3.3 Systemd执行System V启动项(非必须)
systemd 也会查看老式的 systemV init 目录中是否存在相关启动文件,若存在,则 systemd 根据这些配置文件的内容启动对应的服务。
1.读取/etc/inittab文件内容确定启动级别
2.通常情况下先执行/etc/rcS.d/下的脚本,然后是/etc/rcN.d目录(N代表启动级别),最后是/etc/rc.local目录
3.4 启动最后的操作
3.4.1 runlevel3
- 在Systemd将系统启动到multi-user.target所定义的状态时,systemd会启动每一个虚拟终端调用getty,虚拟终端一般有6个,每个虚拟终端初始化tty并请求输入用户名和密码。当在某虚拟终端输入用户名和密码后,其getty会通过/etc/passwd检查是否正确,正确则调用login
- login程序会为用户启动一个设置了环境变量的会话,接着根据/etc/passwd的配置启动用户专用shell
- 一旦用户专用shell启动了,他会在显示命令行提示符前,执行可执行性的配置文件,例如.bashrc
3.4.2 runlevel5
没有配置显示管理器替换getty。仅显示管理器自启
在Systemd将系统启动到multi-user.target所定义的状态时,systemd会启动每一个虚拟终端调用getty,虚拟终端一般有6个,每个虚拟终端初始化tty并请求输入用户名和密码。启动显示管理器(GDM,SDDM,XDM)等等
4. 各脚本启动
- 开机执行/etc/rc.d/rc.local
- 用户登录时读取/etc/profile
- 特定用户登录时执行~/.bash_profile
文件 | 说明 |
---|---|
/etc/profile | 此文件为系统每个用户配置环境信息,当用户第一次登陆时,该文件被执行。并从/etc/profile.d目录下的文件中搜集shell设置 |
/etc/bashrc | 为每一个运行bash shell的用户执行此文件 |
~/.bash_profile | 专用于用户的shell信息,用户登录时执行文件。默认情况下设置变量然后调用.bashrc |
~/.bashrc | 专用于用户自身的bash shell脚本 |
~/.bash_logout | 当每次退出(bash shell)时执行该文件。 |