Linux引导启动详细过程

目 录

1 Linux引导过程

2 BIOS功能描述

3 BootLoader流程分析

4 vmlinuz文件分析

5 initrd (initramfs)文件分析

6 Linux systemd启动流程

7 Cgroups与systemd关系

8 systemd启动目标单元分析

1 Linux引导过程

  1. 主机加电后,系统首先加载BIOS,这个BIOS是烧录在主板上的ROM芯片上的。
  2. BIOS启动后,执行了一些例如开机自检,硬件初始化等工作,然后读取硬盘MBR分区的第 一个扇区(前512字节),其中前446字节储存了一个小程序叫做boot loader,中间的64 字节是磁盘分区表,最后两个字节是固定的0x55AA的文件类型识别标记。
    1. UEFI对比BIOS本身就是一个微型的操作系统,直接读取FAT格式的文件分区(EFI 程序),相比BIOS读取MBR文件分区快了不少。
    2. 常见的boot loader有GRUB、U-boot、UEFI、Etherboot、ARMboot等。
  3. bootloader程序主要用的两个文件,/boot/vmlinuz*内核文件,/boot/initramfs*虚拟文件系 统。
  4. 这两个文件加载到内存运行后,系统会加载真正的文件系统,然后启动1号进程systemd(init)。 这样真正使用的系统就启动了。

2 BIOS功能描述

BIOS(基本输入输出系统)是烧录计算机在主板芯片上的程序,它保存着计算机基本的输入输出的程序,开机后的硬件自检程序和系统自启动程序,它从COMS中读写系统的设置的具体信息,为计算机提供最底层的硬件控制。

BIOS设置硬件参数,设置显示类型核显显示、独立显示和自动,设置串口协议和波特率,常见协议有RS-232,RS-422,RS-485,设置启动硬盘(安装双系统建议使用两块硬盘),控制风扇转速和模式,设置TPM模式,是否上电自启,设置CPU功耗,设置网络的POE供电功能,设置BOOT启动模式传统模式(Legacy Boot Type)、UEFI模式和双模式,设置PXE网络协议启动,设置是否开启门口看门狗-WDT(当检测到系统程序非正常运行后,会强制CPU发送复位信号使整个系统复位)

BIOS也是一些病毒木马攻击的目标

例:

谍影木马支持的BIOS版本非常多,是目前已知的唯一能够感染UEFI主板的木马,恶意

代码可能是有非法份子烧录的主板中的,现阶段只能重新烧录BIOS,才可以彻底清理谍影 木马。

类似的病毒木马还有TrickBot,暗云III,双枪,隐魂,等幽灵木马。日常使用建议保持主 机固件的更新,启用BIOS写保护。

与传统BIOS(汇编语言)具有相同功能的还有UEFI(c语言)。

常见的引导方式:

  1. 传统模式(Legacy Boot Type)开机自检-->GRUB2-->/boot/加载内核和ram文件系统-->systmd (init)进程初始化---/boot/grub2/grub.cfg
  2. UEFI模式开机自检-->GRUB2-->/boot/加载内核和ram文件系统-->systemd进程初始化---/boot/efi/EFI/centos/grub.cfg

传统模式由于是MBR分区无法引导2TB以上的硬盘。

UEFI模式采用GPT分区可以引导大于2TB以上的硬盘并且向下兼容MBR。

3 Bootloader流程分析

boot loader一般分为两个阶段,第一个阶段使用用汇编来实现,它完成了一些依赖于CPU体系结构的初始化,并调用第二阶段的代码,第二阶段通常使用C语言来实现,这样可以实 现更复杂的功能,而且代码会有更好的可读性和移植性。

第一阶段功能

    1. 硬件设备初始化;
    2. 为加载boot loader的第二个阶段代码准备RAM空间;
    3. 复制boot loader的第二个阶段的代码到RAM中;
    4. 设置好栈;
    5. 跳转到第二阶段代码的C入口点(main);

第二阶段功能

    1. 初始化本阶段要使用到的硬件设备;
    2. 检测系统内存映射;
    3. 将内核映像和根文件系统从boot分区读取到RAM空间中;
    4. 为内核设置启动参数;
    5. 调用内核;

4 vmlinuz文件分析

在Linux系统中,vmlinux (vmlinuz) 是一个包含Linux Kernel的静态链接的可执行文件,文件类型可能是Linux接受的可执行文件格式之一(ELF和COFF),vmlinux若要用于调试是则必须要在引导前增加symbol table。

应用场景:

  1. 用于调试,但需要包含调试信息;
  2. 编译出来的内核原始文件,可以被用来制作后面zImage,bzImage等启动Image;
  3. U-Boot不能直接使用vmlinux,可以使用uImage专属镜像;

相关内容:

  1. vmlinux是ELF文件,是编译出来的最原始的内核文件;
  2. vmlinuz是被压缩的linux内核,是可以被引导的(GRUB2);
  3. vmlinuz是一个统称,有两种详细的表现形式:zImage和bzImage(big zImage);
  4. zImage是vmlinuz经过gzip压缩后的文件,适用于小内核;
  5. bzImage是vmlinuz经过gzip压缩后的文件,适用于大内核;

实践:

  1. mkdir /tmp/vmlinuz && cp /boot/vmlinuz-$(uname -r) /tmp/vmlinuz/
  2. cd /tmp/vmlinuz/
  3. od -t x1 -A d vmlinuz-$(uname -r) | grep “1f 8b 08”

本质上,vmlinuz-$(uname -r)是一个gzip压缩文件,但是不能直接用gzip指令解压,因为在这个文件的开头嵌入了gzip的代码。所以首先用指令C找到真正的压缩文件的头部,这个指令的输出如下:

0018864 ac fe ff ff 1f 8b 08 00 00 00 00 00 02 03 ec fd

然后执行下面的指令,其中的18868就是18864+4,这里4是指1f 8b 08前面有4个字节。

dd if=vmlinuz-$(uname -r) bs=1 skip=18868 | zcat > vmlinuz-gakki

objdump -D vmlinuz-gakki >> result

result是一个汇编文件,而vmlinuz-gakki文件本质上是一个可执行程序,可以尝试执行它

chmod +x vmlinuz-gakki && ./vmlinuz-gakki

显示 Segmentation fault(存储器段错误或访问权限冲突)

5 initrd (initramfs)文件分析

Linux允许将一部分内存作为块设备(RAM block device support)。这通常见于完全运行在内存上的Linux的live发行版。Linux的live发行版会卸载光盘并接着加载到内存中,所以在尝试一个新的操作系统或者修复另一个系统时不会伤害到已安装的系统。

initrd是一个内存中的磁盘结构(ramdisk),它把(initrd)文件系统当做可启动文件系统。用于在内核把控制权交给根文件系统上的init应用程序之前挂载所需的文件系统initrd。Linux内核在此根文件系统上执行脚本(通常称为linuxrc),此脚本的工作是准备系统,切换到真正的根文件系统,然后调用init。

initramfs即initram file system,翻译成中文的意思就是初始化ram文件系统,基于tmpfs,是一种大小灵活,直接作用在内存中的文件系统。initramfs包含的工具和脚本,在正式的根文件系统初始化启动之前,就被挂载了。initramfs是可选的,内核编译选项默认开启initramfs。

应用场景:

  1. 加载模块,如第三方驱动;
  2. 定制化启动系统;
  3. 制作一个很小的rescue shell(救援模式);
  4. 内核不能,但是用户态可以完成的命令;

initramfs在内核启动的早期提供一个用户态环境,用于完成在内核启动阶段不易完成的工作。

initramfs包含的工具可以解密抽象层(用于加密的文件系统),逻辑卷管理器,软RAID,

蓝牙驱动程序等。

一个initramfs至少包含一个文件,即systemd,内核将这个文件执行起来的进程设

为main init进程,pid=1。内核挂载initramfs时,文件系统的根分区并没有挂载,所以无法访问 文件系统中的文件。多数的嵌入式设备需要一个shell,那么也会在initramfs打包进一个shell。如 果还需要其他工具或脚本,也可以打包到initramfs。

注意:打包时,必须包含依赖,因为initramfs是一个能够独立运行的ram文件系统。

Tips:dracut是用来制作更轻量化initramfs的工具,它的使用方式跟mkinitrd非常接近,迁移成本较低。

实践:

首先将/boot/initramfs-$(uname -r).img文件复制到/tmp文件夹下

执行file initramfs-5.10.x86_64.img,得到如下结果:

initramfs-5.10.x86_64.img: gzip compressed data, max compression, from Unix, original size 75768832

可以看到它本质上是一个gzip格式的压缩文件

mv initramfs-5.10.x86_64.img initramfs-5.10.x86_64.img.gz

gzip -d initramfs-5.10.x86_64.img.gz

file initramfs-5.10.x86_64.img

显示 initramfs-5.10.x86_64.img: ASCII cpio archive (SVR4 with no CRC)

这是一个cpio文件

执行cpio -idmv < initramfs-5.10.x86_64.img

生成了一个小型的根文件系统(rootfs)。

6 Linux systemd启动流程

在用户真正使用的根文件系统sysroot启动之前,内核用先启动initramfs(虚根)文件系统systemd,systemd完成启动后会挂载sysroot(逻辑根目录),然后切换到根文件系统,再次执行systemd。

Tips:使用命令man bootup 可以查看系统内systemd启动流程的手册原文。

两次执行的systemd流程类似,如下图:

Linux引导启动详细过程_第1张图片

描述:

系统引导涉及许多不同的组件。在上电之后,系统BIOS将立即进行最小的硬件初始化,并将控制权移交给MBR(持久存储中的引导加载程序存储设备)。

然后,这个引导加载程序将从磁盘(或网络)调用操作系统内核。在Linux情况下,该内核(可选)提取并执行初始RAM磁盘映像(initrd)。

例如由Dracut(8),它查找根文件系统(可能为此使用systemd(1))。找到并挂载根文件系统后,initrd将控制权移交给主机的系统管理器(例如systemd(1))。

存储在操作系统映像中,然后该映像负责检测所有剩余的硬件,挂载所有必要的文件系统并生成所有配置的服务。

在关闭时,系统管理器停止所有服务,卸载所有文件系统(卸载支持它们的存储技术),然后(可选地)跳转回卸载/卸载根目录的initrd代码文件系统及其所在的存储。最后一步是关闭系统的电源。

系统管理器(systemd)启动:

在引导时,操作系统映像上的系统管理器负责初始化操作系统所需的文件系统、服务和驱动程序。在systemd(1)系统上,这个进程被拆分。

这个过程被分散到以systemd.target为单元的多个步骤中。有关目标单元的详细信息,参见system .target(5)。启动过程是高度并行的,以便特定目标的启动顺序的单元是不确定的,但仍然坚持有限数量的排序结构。

当systemd启动系统时,它将激活所有依赖于default的单元。目标(以及递归地使用这些依赖的所有依赖项)。Target只是一个别名。

graphical.target(图形界面)或multi-user.target(多用户模式或文本模式),这取决于系统是配置为图形UI还是仅配置为文本控制台,graphical.target和multi-user.target对应systemV中的运行级别init5 和init3。

目录/etc/systemd/system/multi-user.target.wants/,这里的服务是用户自定义的systemd的开机启动项。

目录/usr/lib/systemd/system/multi-user.target.wants/,是系统开机启用的systemd。

该目录结构:

  1. dbus.service:D-Bus总线守护程序服务;
  2. getty.target:启动虚拟终端实例(或容器终端实例)并初始化它们;
  3. plymouth-quit.service:普利茅斯退出服务。plymouth通过内核中“内核模式设置”(Kernel Mode-Setting)和显示支持,它容易定制和无闪烁的图形启动模式,可以提供更好系统启动的界面;
  4. plymouth-quit-wait.service:普利茅斯退出守护进程;
  5. systemd-ask-password-wall.path:负责查询用来密码;
  6. systemd-logind.service:负责管理用户登录的系统服务;
  7. systemd-update-utmp-runlevel.service:切换运行级别时在utmp和wtmp中记录切换信息;
  8. systemd-user-sessions.service:负责控制系统是否允许登录;

下图是这些众所周知的单元及其在启动逻辑中的位置的结构概述。箭头描述了哪些单元被启动,并在其他单元之前排序。

在接近图表底部的单元之前开始。

Linux引导启动详细过程_第2张图片

重点强调通常用作引导目标的目标单元。这些单元是作为目标目标的很好的选择,例如将它们传递给systemd.unit = kernel命令行选项(参见systemd(1))或将创建default.target。

timers.target被基本拉入。异步的目标。这允许计时器单元依赖于只有在引导之后才可用的服务。

在初始ram磁盘(initrd)中启动:

初始RAM磁盘实现(initrd)也可以使用systemd设置。在这种情况下,initrd内部的引导遵循以下结构。

initrd中的默认目标器为initrd.target。启动过程的开始与系统管理器的启动过程相同(参见上面),直到它到达basic.target。从那里,systemd接近特殊目标。

initrd.target。在挂载任何文件系统之前,必须确定系统是将从休眠状态恢复还是继续正常引导。这是通过[email protected]必须完成的。

在local-fs-pre.target之前完成。因此在检查完成之前不能挂载任何文件系统。如果根设备可以挂载,当根设备可用时,启动initd-root-device.target。

/sysroot sysroot.mount单元开始活动到initrd-root-fs.target。initrd-parse-etc.service扫描/sysroot/etc/fstab,查找/usr挂载点和可以用的挂载点。

x-initrd.mount选项。找到的所有条目都挂载在到/sysroot和initrd-fs.target。initrd-cleanup.service隔离到initrd-switch-root.target,可以在其中运行清理服务。

随着最后一步,initrd-switch-root.service被激活,将导致系统将根目录切换到/sysroot。

Linux引导启动详细过程_第3张图片

系统管理器关闭:

使用systemd的系统关闭也包含各种目标单元,并应用了一些最小顺序结构。

Linux引导启动详细过程_第4张图片

强调了常用的系统关闭目标。

system-halt.service(8),system-reboot.service,systemd-poweroff.service,systemd-kexec.service将把系统和服务器管理器(PID 1)转换到系统关闭的第二阶段(已实现)。

在system -shutdown二进制文件中,它将卸载所有剩余的文件系统,杀死所有剩余的进程并释放所有剩余的资源,简单而稳健,不接受任何服务或单元。

再把概念考虑进去。在这一点上,常规的应用程序和资源通常已经终止和释放,因此,第二阶段只能作为无法停止的一切的安全网。

或者在上面描述的基于单元的主要停机阶段由于某种原因释放。

7 Cgroups与systemd关系

当Linux的init系统发展到systemd之后,systemd与cgrops发生了融合(或者说systemd提供了cgroups的使用和管理接口),systemd管理的东西非常多。

描述:

Cgroups是Linux内核提供的一种机制。

systemd依赖Cgroups:

Cgroups分为两个方面,层级机构(A)和资源控制(B)。首先cgroups是以层级结构组织并标识进程的一种方式,同时它也是在该层级结构上执行资源限制的一种方式。我们简单的包cgroups的层级结构称为A,把cgroups的资源控制能力称为B。

对于systemd来说,A是必须的,如果没有A,systemd将不能很好的工作。而B则是可选的,如果你不需要对资源进行控制那么在编译Linux内核时完全去掉B相关的编译选项。

在系统开机阶段,systemd会把支持的controllers(控制器子系统)挂载到默认的/sys/fs/cgrop/目录下面。

Cgroups的默认层级:

通过将cgroup层级系统与systemd unit树绑定,systemd可以把资源管理的设置从进程级别移动至应用程序级别。

默认情况下,systemd会自动创建slice、scope、和service unit的层级(slice、scope和service都是systemd和unit类型),来为cgroup树提供统一的层级结构。

系统中运行的所有进程,都是systemd init进程的子进程。

在资源管控方面,systemd提供了三种unit类型:

  1. service:一个或一组进程,由 systemd 依据 unit 配置文件启动。service 对指定进程进行封装,这样进程可以作为一个整体被启动或终止。
  2. scope:一组外部创建的进程。由进程通过fork()函数启动和终止、之后被systemd在运行是注册的进程,scope会将其封装。例如:用户会话、容器和虚拟机都被认为是scope。
  3. slice:一组按层级排列的unit。slice并不包含进程,但会组件一个层级,并将scope和service都放置其中。真正的进程包含在scope或service中。在这一被划分层级的树中,每一个slice单元的名字对应通向层级中一个位置路径。

我们可以通过systemd-cgls命令查看cgroups的层级结构:

Linux引导启动详细过程_第5张图片

service、scope和slice unit被直接映射到cgroup树中的对象。当这些unit被激活时,它们或直接一一映射到有unit名建立的cgroup路径中。例如,crond.service属于system.slice,会直接映射到cgroup system.slice/crond.service/中。

8 systemd启动目标单元分析

下图显示为操作系统瀑布状的启动流程:

Linux引导启动详细过程_第6张图片

  1. -.slice:根slice。它通常不直接包含单元,但可以用于设置整个树的默认值。
  2. system.slice:所有系统service的默认位置;
  3. -.mount:根挂载点,即路径的挂载单元,在整个系统启动期间,此单元一直处于活动状态,因为此挂载点是基本用户空间的运行位置;
  4. dm-event.socket:dm-event.socket套接字服务;
  5. lvm2-monitor.service:磁盘lvm2分区服务。通常在系统引导时启动,在后台运行,并在需要时自动唤醒;
  6. Local-fs-pre.target:本地文件系统;
  7. run-wlm.mount:运行并挂载工作负载器(WLM);
  8. local-fs.target:自动将类型的依赖关系添加到所有引用此目标单元的本地挂载点的挂载单元中。此外,它还会为 中列出的具有 mount 选项集的挂载,向此目标单元添加类型的依赖关系。Before=Wants=/etc/fstabauto
  9. import-state.service:导入服务状态;
  10. systemd-tmpfiles-setup.service:创建、删除、和清理易失性的临时文件和目录;
  11. audit.service:audit服务可以对用户行为进程监控。比如修改或者删除文件这样的行为,通过系统日志是无法查看到的,通过配置audit服务就可以实现监控功能;
  12. systemd-update-utmp.service:启动、切换运行级、关机时更新审计日志以及登录和退出记录;
  13. sysinit.target:systemd会自动将类型和此目标单元的依赖项添加到所有服务(带有的服务的依赖项除外)。Requires=After=DefaultDependencies=no
  14. iscsiuio.socket:用户空间I/O驱动程序接口;
  15. sockets.target:一个特殊的目标单元,用于设置所有在引导后处于活动状态的套接字单元。在安装期间,可进行套接字激活的服务应向此单元添加其套接字单元的依赖关系。最好通过套接字单元的[install]部分进行配置。Wants=WantedBy=sockets.target
  16. basic.target:一个特殊的目标单元,涵盖基本启动。systemd会自动将此目标单元类型的依赖关系添加到所有服务(带有的服务除外)。After=DefaultDependencies=no
  17. polkit.service:策略工具包服务,polkit是一个应用程序级工具包,用于定义和处理允许非特权进程与特权进程对话的策略,它是一个框架,用于集中决策过程,已授予对非特权应用程序特权操作的访问权限;
  18. firewalld.service:防火墙服务配置文件。最重要的配置选项是端口、模块和目标地址;
  19. network-pre.target:此被动目标单元可能希望在设置任何网络服务运行之前拉入,例如防火墙。所有网络管理软件在此目标之后自行订购,但不将其拉入;
  20. network.target:这个单元指向网络功能何时可用,它只是非常简单的定义了一下。但是,以下情况至少应适用。
  21. 在启动时,任何已配置的合成网络设备(即不是需要硬件显示和探测的物理设备,而是虚拟设备,如桥接设备和类似的设备,以编程方式创建),在达到此目标是应进行分配。这些接口在没有到达时已经完成IP级别的配置。
  22. 在关闭时,在网络关闭之前,订购的单元将被停止— —到那时它不可能设置任何级别— —被关闭。因此,在写入需要在关闭时进行网络访问的服务文件时,它很有用,这些文件应在此目标之后自行排序,但不要将其拉入。
  23. 必须强调的是,在启动时,不能保证基于硬件的设备在达到此目标时已经出现,甚至无法获得完整的IP配置;
  24. tuned.service:动态系统调优服务;
  25. multi-user.target:用于设置多用户系统(非图形)的特殊目标单元。多用户系统所需的设备应在安装期间为设备添加相应的依赖关系,最好通过设备的[install]进行配置,Wants=WantedBy=multi-user.target;
  26. graphical.target:用于设置图形登录屏幕的特殊目标单元。图形登录所需的设备应在安装期间为设备添加相应的依赖关系。最好通过设备的[install]进行配置,Wants=multi-user.target WantedBy=graphical.target;

你可能感兴趣的:(linux)