本文内容预告:

  • 开机启动流程图预览
  • 按下电源和固件阶段
  • 内核启动阶段
  • 内核启动后,用户登录前

计算机启动流程可以分为几个大阶段:

  • 内核加载前

    • 本阶段和操作系统无关,Linux或Windows或其它系统在这阶段的顺序是一样的
  • 内核加载中-->内核启动完成
  • 内核加载后-->系统环境初始化完成
  • 终端加载、用户登录

这几个阶段中又有很多小阶段,每个阶段都各司其职。本文将主要介绍UEFI+systemd环境下的Linux系统启动流程,在需要的时候,本文也会稍微介绍一些Bios+MBR+SysV的内容以作比较和整合。

开机流程图预览

下图是开机的全局流程图,具体的细节后文再详细描述。

按下电源和固件阶段

按下电源,计算机开始通电,最重要的是要接通cpu的电路,然后通过cpu的针脚让cpu运行起来,只有cpu运行起来才能执行相关代码跳到第一个程序:bios或uefi上,并将CPU控制权交给bios或uefi程序。

下面不考虑从网络启动系统的方式,只考虑启动本地系统。

使用bios的固件阶段

对于BIOS来说,BIOS的工作包括:

  • (1).POST,即加电对部分硬件进行检查
  • (2).POST自检之后,BIOS初始化一部分启动所需的硬件(比如会启动磁盘,某些机器可能还会启动键盘)
  • (3).根据启动顺序找到排在第一位的磁盘
  • (4).BIOS跳转到所选择磁盘的前446字节,这446字节的代码是第一个bootloader程序,BIOS加载bootloader并将CPU控制权交给bootloader程序
    • 磁盘的第一个扇区(前512字节)称为MBR,其中前446字节是bootloader程序,中间64字节是磁盘分区表,最后两个字节是固定0x55AA的魔数标记,标记该磁盘的MBR是否有效,如果无效,则读取启动顺序中的第二位磁盘
    • MBR中的bootloader是硬编码在磁盘第一个扇区中的,像grub类的启动管理器会安装各个阶段的boot loader,包括这个MBR bootloader
  • (5).执行MBR中的bootloader程序,这个bootloader可能会直接加载内核,也可能会跳转到更多的bootloader程序上
    • 因为MBR中的bootloader只有446字节,代码量非常有限,所以有些系统会使用多段bootloader的方式。比如grub在安装MBR的时候,所安装的MBR bootloader最后的代码逻辑是跳转到下一个阶段的bootloader(也是grub安装的),如果下一个阶段的bootloader还不够,那么还可以有更多阶段的bootloader。这种模式称为链式启动
    • 因为内核镜像和启动管理器配置文件等内核启动所必须的文件都在boot分区下,所以中间某个bootloader程序会加载boot分区所在文件系统驱动,使之能够识别boot分区并读取boot分区中的文件
  • (6).最后一个bootloader将获取内核启动参数(比如从boot分区读取grub配置文件获取内核参数),并加载操作系统的内核镜像(grub配置文件中指定了内核镜像的路径),同时向内核传递内核启动参数,然后将CPU控制权交给内核程序

至此,内核已经加载到内存中,并进入到了内核启动阶段,CPU控制权将转移到内核,内核开始工作。

注:Bios典型支持的是MBR分区类型,但也支持GPT分区类型。UEFI典型支持的是GPT分区类型,但也支持MBR。

从系统启动的角度看,无需在乎是MBR还是GPT,其基本目的都是找到各阶段的bootloader。

使用uefi的固件阶段

UEFI支持读取分区表,也支持直接读取一个文件系统。UEFI不会启动任何MBR扇区中的bootloader(即使有安装MBR),而是从非易失性存储中找到启动条目(boot entry)并启动它。

典型的UEFI支持的非易失性存储有FAT12、FAT16和FAT32文件系统(即EFI系统分区),但是发行商也可以加入额外的文件系统,只要提供对应文件系统的驱动程序即可。比如Macs支持HFS+文件系统。此外,UEFI也支持ISO-9660光盘。

UEFI会启动EFI系统分区中的EFI程序,所谓的EFI程序即类似bootloader的程序,比如单纯的bootloader程序,类似GRUB的启动管理器、UEFI shell程序等(作为systemd系列文章之一,在此有必要一提,systemd-boot工具也可以制作基于UEFI的bootloader)。这些程序通常位于efi系统分区下的/EFI/vendor_name目录中,不同发行商可加入不同的efi程序。在EFI分区的/efi/目录下还有一个boot目录,这个目录中保存了所有的启动条目。

如下图,EFI目录下除了Boot目录外,还有4个发行商(Acronis、deepin、Microsoft、Ubuntu)各自的EFI程序目录。

当使用UEFI固件时,CPU通电后,UEFI的工作内容主要包括:

  • (1).POST,即加电对部分硬件进行检查
  • (2).POST自检之后,uefi程序初始化一部分启动所需的硬件(比如会启动磁盘,某些机器可能还会启动键盘)
  • (3).uefi读取保存在EFI系统分区中的启动记录,从而决定启动哪个EFI程序以及从哪里启动该程序
    • 上文已经介绍了EFI程序是什么
    • EFI分区中的\efi\boot\bootx64.efi(64位UEFI)可查找要启动的EFI程序
  • (4).UEFI启动efi程序,并获取内核启动参数、加载内核镜像到内存,等(可参考上文件BIOS阶段最后描述的过程)

至此,内核已经加载到内存中,并进入到了内核启动阶段,CPU控制权将转移到内核,内核开始工作。

内核启动阶段

到目前为止,内核已经被加载到内存掌握了控制权,且收到了boot loader最后传递的内核启动参数,包括init ramdisk镜像的路径

但注意,所有的内核镜像都是以bzImage方式压缩过的,压缩后CentOS 6的内核大小大约为4M,CentOS 7的内核大小大约为5M,CentOS 8的内核大小约9M。所以内核要能正常运作下去,它需要进行解压释放。

#

当内核真正开始运行后,将从/boot分区找到initial ram disk image(后面简称init ramdisk)并解压。init ramdisk要么是initrd,要么是initramfs,它们可使用dracut工具生成,早期系统用initrd比较多,因为现在就会都用initramfs,所以后面可能会以initramfs来描述init ramdisk。

init ramdisk解压之后就得到了内核空间的根文件系统(这是启动过程中的早期根分区,也称为虚根)。对于使用systemd的系统来说,得到虚根之后就可以调用集成在initramfs中的systemd程序,其PID=1。从现在开始,就进入了早期的用户空间(early userspace),systemd进程可以在此时做一些内核启动剩余的必要操作。

完成内核启动的必要操作后,systemd最后会将虚根切换为真实的根分区(即系统启动后用户看到的根分区),并进入真正的用户空间阶段。systemd从此开始就成为了用户空间的总管进程,也是所有用户空间进程的祖先进程。

详细分析内核启动阶段

上面描述的内核启动过程很简单,但里面还有一些『细节』值得思考。

1.如何找到init ramdisk

实际上,内核运行后,会创建一个负责内核运行环境初始化的进程,该进程会根据boot loader传递过来的内核启动参数找到init ramdisk的路径。正常情况下,该路径在boot分区中。之所以能够读取Boot分区是因为在固件阶段,bootloader程序已经加载了boot分区的文件系统驱动。

2.为什么要用init ramdisk

因为直到现在还无法读取根分区,甚至目前还没有根分区的存在,但显然,之后是要挂载根分区的。但是要挂载根分区进而访问根分区,要求知道根分区的类型(比如是xfs还是ext4),从而装在根文件系统的驱动模块。

由于不同用户在安装操作系统时,可能会选择不同的文件系统作为根文件系统的类型,所以不同用户的操作系统在内核启动过程中,根文件系统类型是不同的。如何知道该用户所装的操作系统的根文件系统是哪种类型的?

事实上,在安装操作系统的最后阶段,会自动收集当前所装操作系统的信息,并根据这些信息动态生成一些文件,包括用户所选择的根文件系统的类型以及对应的文件系统驱动模块。这个过程收集的内容会保存在init ramdisk镜像中。

如下图,是某次我安装CentOS 7过程中截取下来的生成initramfs镜像文件的图片。

3.内核中根分区的来源

内核会将init ramdisk镜像解压在一个位置下,这个位置称为rootfs,即根文件系统,这个根分区称为虚根,因为这个根分区和系统启动后用户看到的根分区不是同一个根分区。

对于使用initramfs镜像的ramdisk来说,这个rootfs即为ramfs(ram file system),它是一个在解压initramfs镜像后就存在且挂载的文件系统,但是系统启动之后用户并不能找到它,因为在内核启动完成后它就会被切换到真实的根文件系统。

用户也可以手动解压/boot/initramdisk-xxx.img镜像:

$

可以想象一下,init ramdisk镜像解压在/tmp/hhh目录下,那么这个目录就可以看作是在内核启动过程中的rootfs。解压得到的根目录和系统启动后的根目录内核很相似:

$

再深入一点看,会发现ramdisk中已经生成了我当前操作系统根分区和boot分区的驱动模块。

#

4.PID=1进程的来源

或者说,PID=1的init程序集成在init ramdisk中?在内核启动过程中就加载了它?

对于早期的initrd来说,init程序并没有集成在initrd中,所以那时的内核会在装载完根分区驱动模块后去根分区寻找/sbin/init程序并调用它。且对于使用initrd的启动过程来说,加载/sbin/init的时机比较晚,很多内核启动过程中的环境都是由内核进程而非init进程完成的。

对于initramfs,它已经将init程序集成在init ramdisk镜像文件中了。如下:

$

仔细观察上面的文件结构。init是一个指向systmed的软链接(因为这里使用的是systemd而不是SysV),此外还有几个重要的目录proc、sys、dev、sysroot。

由于内核加载到这里已经初始化一些运行环境了,有些环境是可以保留下来的,这样系统启动后就能直接使用这些已经初始化的环境而无需再次初始化。比如内核的运行状态等参数保存在虚根的/proc和/sys(当然,内核运行环境是保存在内存中的,这两个目录只是内核暴露给用户的路径),再比如,已经收集到的硬件设备信息以及设备的运行环境也要保存下来,保存的位置是/dev

sysroot则是最重要的,它就是系统启动后用户看到的真正的根分区。没错,真正的根分区只是ramdisk中的一个目录。

systemd在内核启动阶段做的事

在内核启动阶段,当调用了集成在initramfs中的systemd之后,systemd将接管后续的启动操作。但是在这个阶段,systemd具体会做哪些操作呢?

分析systemd在启动阶段所做的事之前,最好对启动流程中的systemd能有一个全局的了解。

注:下图适用于即将解释的内核启动阶段中systemd的流程,也适用于内核启动完成后systemd的流程。

在启动过程中,systemd有几个大目标,每个目标以.target表示。systemd的target主要作用是对多个任务进行分组,比如basic.target中,可能包含了很多任务。

  • 第一个大目标:sysinit.target
    sysinit.target主要目的是读系统运行环境做初始化,初始化完成后进入下一个大目标
  • 第二个大目标:basic.target
    basic.target的作用主要是在环境初始化完成后执行一些基本任务,算是做一些早期的开机自启动任务,basic.target完成后进入第三个大目标
  • 第三个大目标:『default.target运行级别』
    default.target是一个软链接,链接到不同的target表示进入不同的『运行级别』,运行级别阶段的目的是为最终的登录做准备:

    • 如果是内核启动过程中(内核启动时调用了集成在initramfs中的systemd就处于这个过程),那么大目标是initrd.target,该target的目标是为后续虚根切换到实根做初始化准备,比如检查并挂载根文件系统,最终虚根切换实根,并进入真正的用户空间,也即完成了内核启动或内核已经成功登录
    • 如果不是内核启动过程中,可能会选择不同的『运行级别』,通常是图形终端的graphical.target,或者多用户的运行级别multi-user.target,它们的目标都是完成系统启动,并准备让用户登录系统

所以,在内核启动阶段,要分析systemd的工作流程,沿着sysinit-->basic-->initrd-->kernel launched这条路线即可。

回到在内核启动阶段,systemd接管后续的启动操作,具体会做哪些事呢?在man bootup手册中给出了内核启动阶段中systemd的工作流程图。

如下图。

注意,图中所有涉及到的unit文件均来自initramfs镜像解压后的目录,也即虚根,而不是来自真实的根文件系统,因为现在还没有真实的根文件系统。例如/usr/lib/systemd/system/sysinit.target文件。

对于已经启动完成的正常系统来说,sysinit.target是用于做系统初始化的,basic.target则是系统初始化完成后执行的一些基本任务,比如启动所有定时器任务,开始监控指定文件等,算是系统启动过程中早期的开机自启动任务。

但是在内核启动阶段,sysinit.target和basic.target所代表的含义,显然与启动完成后这两个文件代表的含义有所不同。在内核启动阶段,sysinit.target中的sys指的是内核阶段的虚拟系统,basic则代表ramdisk决定要执行的基础任务。

换句话说,在内核启动阶段,systemd也将initramfs所启动的环境看作是一个操作系统,只不过是一个虚拟的操作系统。

当basic.target完成后,进入initrd.target,之所以是initrd.target,是因为initramfs中的default.target软链接指向的是initrd.target。

$

下图描述了initrd.target阶段的工作流程。

在initrd阶段,systemd为后续切换到真实的根文件系统做准备,比如检查根文件系统并将其挂载在/sysroot下,再比如从/etc/fstab中找出部分需要在这个阶段挂载的分区。如果一切没问题,将进入最后的阶段:从initramfs的虚根/切换到实根/sysroot

从此开始,/sysroot将代表真实的根文件系统,systemd也将从这个根文件系统调用init程序(systemd)替换当前的systemd进程(所以PID仍然为1)。从此内核引导阶段退出舞台,开始进入真正的用户空间,systemd进程也将在这个用户空间开始下一阶段的流程:sysinit->basic->default->login

所以,总结一下内核启动阶段中systemd接管控制权后到用户登录中间的流程。如下图。虽然图中内核启动之后的阶段还未展开介绍,但我想大家应该能理解,即使不理解也没关系,稍后会展开。

内核启动后,用户登录前

当systemd(这个是来自真实根文件系统的systemd进程)进入到用户空间后,systemd将执行下一轮工作流程,全局路线为:sysinit.target->basic.target->default.target->...->login

其中default.target是一个软链接,一般指向graphical.target(图形界面)或multi-user.target(多用户模式),对应于SysV系统中的『运行级别』阶段。

需注意,在这里涉及到的所有unit文件都来自于真实的根文件系统,例如/usr/lib/systemd/system/sysinit.target。而内核启动阶段systemd工作路线中涉及到的unit文件都来自于initramfs构建的虚根,例如/usr/lib/systemd/system/sysinit.target,而且前文也提到过,在systemd眼中,initramfs构建的也是一个系统,只不过是虚拟系统,最终systemd会从这个虚拟系统中切换到真实的系统中,切换的内容主要包括两项:切换根分区,切换systemd进程自身。

在流程的每一个大阶段,和前面介绍的initramfs中的systemd是类似的。

basic.target完成后,将通过default.target决定进入哪一个『运行级别』。如果是进入graphical.target,那么会启动和图形终端相关的服务任务,如果是进入multi-user.target,那么:

$

其中几项需要注意:

  • getty.target:启动虚拟终端实例(或容器终端实例)并初始化它们
  • systemd-logind:负责管理用户登录操作
  • systemd-user-sessions:控制系统是否允许登录
  • systemd-update-utmp-runlevel:切换运行级别时在utmp和wtmp中记录切换信息
  • systemd-ask-password-wall:负责查询用户密码

除了这几个multi-user.target所依赖的服务外,在/etc/systemd/system/multi-user.target.wants下也有需要启动的服务,这里的服务是用户定义的systemd类的开机自启动服务。

$

不仅如此,为了兼容早期sysV的rc.local开机自启动功能,systemd会检查/etc/rc.local是否具有可执行权限,如果具备,systemd会在此阶段自动执行rc.local中的命令。

参考资料

  • https://wiki.archlinux.org/index.php/Arch_boot_process
  • https://web.archive.org/web/20150430223035/http://archlinux.me/brain0/2010/02/13/early-userspace-in-arch-linux/
  • https://developer.ibm.com/articles/l-linuxboot/
  • man bootup、man systemd.special


历史文章:

取代cron和at的定时任务:systemd timer

systemd时代的/etc/fstab

systemd path实时监控文件和目录的变动

systemd做时间同步

easyuefi只能在基于uefi启动的_systemd时代的开机启动流程(UEFI+systemd)相关推荐

  1. “EasyUEFI只能在基于(U)EFI启动的Windows操作系统上安装”解决方案

    我们在安装EasyUEFI时可能会爆出错误弹窗--"EasyUEFI只能在基于(U)EFI启动的Windows操作系统上安装",如下图所示.解决这一问题的思路也很简单,那就是安装E ...

  2. Linux Redis自动启动,Redis开机启动,Linux Redis设置开机启动

    Linux Redis自动启动,Redis开机启动,Linux Redis设置开机启动 >>>>>>>>>>>>>> ...

  3. win7开机启动项怎么设置 电脑开机启动项在哪里设置

    win7开机启动项怎么设置 电脑开机启动项在哪里设置 现在的软件很多安装程序都是默认的开机启动,电脑上如果软件的开机启动项多了就会使得电脑的开机速度很慢,这个时候就需要大家手动来设置开机启动项了,那电 ...

  4. linux redis自动重启,Linux Redis自动启动,Redis开机启动,Linux Redis设置开机启动

    Linux Redis自动启动,Redis开机启动,Linux Redis设置开机启动 >>>>>>>>>>>>>> ...

  5. easyuefi只能在基于uefi启动的_UEFI启动下的Windows10 安装 Ubuntu18.04教程

    现在大多数电脑的主板采用的是EFI启动,网上很多基于BIOS启动的Ubuntu的安装教程已经不再适用.这几天捣鼓了很久终于在自己的船上装好了Ubuntu.总结一下安装的过程,供大家参考. 制作UEFI ...

  6. easyuefi只能在基于uefi启动的_UEFI模式有什么优点?安装系统首选UEFI启动,电脑系统速度更流畅...

    legacy启动是什么?UEFI启动是什么?可能大部分朋友对这个概念都比较模糊,UEFI可以做什么呢?有什么具体的作用?似乎经常可以在BIOS设置里会见到它,毕竟从几年前开始越来越多的电脑都使用上了带 ...

  7. easyuefi只能在基于uefi启动的_云计算学习体系-1.1-计算机硬件基础扩展知识BIOS/UEFI/MBR/GPT...

    我们每天都在用电脑,很多人可能每天都要面对多次Windows的启动过程,可是您知道在Windows的启动过程背后,你真真了解过吗?上文中我们认识了计算机的基本硬件,其中讲到BIOS和UEFI,下次内容 ...

  8. easyuefi只能在基于uefi启动的_PE启动盘的制作

    视频教程: PE启动盘制作方法​www.bilibili.com 一:什么是PE 根据维基百科的解释,PE,Windows预先安装环境(Microsoft Windows Preinstallatio ...

  9. easyuefi只能在基于uefi启动的_只需2个命令,就能将win10 BIOS启动方式转换为UEFI,你见过吗!...

    平常如果大家有使用启动U盘进行重装系统的话,就会知道其实U盘有三种启动方式:快捷键,BIOS还有UEFI.快捷键这种方式最为简单,而且容易操作,只需要在开机界面中按下提前查询到的快捷按键就能成功启动U ...

最新文章

  1. Ubuntu 上安装rust
  2. sklearn快速入门教程:(三)机器学习的通用模式及实现方法
  3. 出国?上研?工作?回家种田?(一) 出国看世界
  4. Spring MVC实现文件下载
  5. 查看表状态及索引碎片语句
  6. android 上下翻页素材,【Android 进阶】仿抖音系列之翻页上下滑切换视频(四)...
  7. php怎么实现简单的mvc框架,php实现简单的MVC框架实例
  8. hive基于多列去重操作
  9. php基本类,php入门一之十大基本类型
  10. 湖北工程学院计算机宿舍,湖北工程学院宿舍条件,宿舍环境图片(10篇)
  11. 基于JAVA+Swing+MYSQL的在线考试系统
  12. Netty工作笔记0037---主从Reactor多线程
  13. 【转】C#字符串转换为日期
  14. linux下usb无线网卡对比
  15. 在Foxit PDF Editor里面创建一份A4新文档,如何设置页面?
  16. Cloud 2.0时代,华为云EI助力内蒙煤焦化产业走向智能
  17. php数组键值对是什么意思_php数组中键值对怎么理解呢?
  18. Windows10家庭版升级至专业版
  19. stm32--数码管
  20. [博学谷学习记录]超强总结,用心分享|Hive的压缩格式

热门文章

  1. 优化了一个SQL---纪念一下
  2. bjtuOJ1018 Campus Singing Competition
  3. ROS机器人自主导航详解
  4. java程序员的大数据之路(6):定制的Writable类型
  5. ubuntu安装计算器
  6. 有n个人,顺序排列, 并首尾相连围成一圈。从第一个人开始报数(从1到4),凡报到4的人退出圈子+扩展版本
  7. c++小程序之加减法练习
  8. WinForm 关于任务栏图标进程结束后图标无法自动清除的问题!
  9. flex添加gif图片
  10. UI设计和平面设计学哪个好?平面设计转行做UI设计容易吗?