Linux 启动和启动过程简介

有没有想过系统需要做什么才能准备好运行应用程序? 这就是底层发生的事情。
740 位读者喜欢这篇文章。
The boot process

Penguin, Boot。 由 Opensource.com 修改。 CC BY-SA 4.0。

理解 Linux 启动和启动过程对于配置 Linux 和解决启动问题非常重要。 本文概述了使用 GRUB2 引导加载程序 的引导顺序以及由 systemd 初始化系统 执行的启动顺序。

实际上,启动 Linux 计算机并使其可用的过程需要两个事件序列:引导启动引导序列在计算机开机时开始,并在内核初始化和 systemd 启动时完成。 然后,启动过程接管并完成使 Linux 计算机进入可操作状态的任务。

总的来说,Linux 启动和启动过程相当容易理解。 它由以下步骤组成,将在以下部分中更详细地描述。

  • BIOS POST
  • 引导加载程序 (GRUB2)
  • 内核初始化
  • 启动 systemd,所有进程的父进程。

请注意,本文涵盖 GRUB2 和 systemd,因为它们是大多数主要发行版的当前引导加载程序和初始化软件。 历史上曾使用过其他软件选项,并且仍然在某些发行版中找到。

引导过程

引导过程可以通过几种方式启动。 首先,如果电源已关闭,则打开电源将开始引导过程。 如果计算机已经在运行本地用户(包括 root 用户或非特权用户),则用户可以使用 GUI 或命令行启动重启来以编程方式启动引导序列。 重启将首先执行关机,然后重新启动计算机。

BIOS POST

Linux 引导过程的第一步实际上与 Linux 无关。 这是引导过程的硬件部分,对于任何操作系统都是相同的。 当首次向计算机供电时,它会运行 POST(加电自检),这是 BIOS(基本 I/O 系统)的一部分。

当 IBM 在 1981 年设计第一台 PC 时,BIOS 的设计目的是初始化硬件组件。 POST 是 BIOS 的一部分,其任务是确保计算机硬件功能正常。 如果 POST 失败,则计算机可能无法使用,因此引导过程不会继续。

BIOS POST 检查硬件的基本可操作性,然后发出 BIOS 中断 INT 13H,该中断在任何连接的可引导设备上查找引导扇区。 它找到的第一个包含有效引导记录的引导扇区被加载到 RAM 中,然后控制权转移到从引导扇区加载的代码。

引导扇区实际上是引导加载程序的第一个阶段。 大多数 Linux 发行版使用三个引导加载程序:GRUB、GRUB2 和 LILO。 GRUB2 是最新的,并且比其他旧选项更频繁地使用。

GRUB2

GRUB2 代表 “GRand Unified Bootloader, version 2”(GRand 统一引导加载程序,版本 2),它现在是大多数当前 Linux 发行版的主要引导加载程序。 GRUB2 是使计算机足够智能以找到操作系统内核并将其加载到内存中的程序。 因为说 GRUB 比 GRUB2 更容易书写和说出,所以我可能会在本文档中使用术语 GRUB,但我将指的是 GRUB2,除非另有说明。

GRUB 的设计与 multiboot 规范 兼容,该规范允许 GRUB 引导多个版本的 Linux 和其他免费操作系统; 它还可以链式加载专有操作系统的引导记录。

GRUB 还允许用户选择从任何给定 Linux 发行版的多个不同内核中引导。 如果更新的内核以某种方式失败或与重要的软件不兼容,这提供了引导到先前内核版本的能力。 可以使用 /boot/grub/grub.conf 文件配置 GRUB。

GRUB1 现在被认为是遗留版本,并且在大多数现代发行版中已被 GRUB2 取代,GRUB2 是 GRUB1 的重写版本。 基于 Red Hat 的发行版在 Fedora 15 和 CentOS/RHEL 7 左右升级到 GRUB2。 GRUB2 提供与 GRUB1 相同的引导功能,但 GRUB2 也是类似大型机的基于命令的预操作系统环境,并且在预引导阶段允许更大的灵活性。 GRUB2 使用 /boot/grub2/grub.cfg 进行配置。

GRUB 的主要功能是使 Linux 内核加载到内存中并运行。 两个版本的 GRUB 的工作方式基本相同,并且具有相同的三个阶段,但我将使用 GRUB2 来讨论 GRUB 如何完成其工作。 GRUB 或 GRUB2 的配置以及 GRUB2 命令的使用不在本文的范围之内。

尽管 GRUB2 没有正式使用阶段符号来表示 GRUB2 的三个阶段,但以这种方式引用它们很方便,因此我将在本文中使用。

阶段 1

如 BIOS POST 部分所述,在 POST 结束时,BIOS 搜索连接的磁盘以查找引导记录,通常位于主引导记录 (MBR) 中,它将找到的第一个引导记录加载到内存中,然后开始执行引导记录。 引导程序代码,即 GRUB2 阶段 1,非常小,因为它必须与分区表一起安装在硬盘驱动器上的第一个 512 字节扇区中。 在 经典通用 MBR 中,为实际引导程序代码分配的总空间为 446 字节。 阶段 1 的 446 字节文件名为 boot.img,不包含单独添加到引导记录中的分区表。

由于引导记录必须非常小,因此它也不是很智能,并且不了解文件系统结构。 因此,阶段 1 的唯一目的是定位和加载阶段 1.5。 为了实现此目的,GRUB 的阶段 1.5 必须位于引导记录本身和驱动器上第一个分区之间的空间中。 将 GRUB 阶段 1.5 加载到 RAM 中后,阶段 1 将控制权交给阶段 1.5。

阶段 1.5

如上所述,GRUB 的阶段 1.5 必须位于引导记录本身和磁盘驱动器上第一个分区之间的空间中。 出于技术原因,此空间在历史上一直未被使用。 硬盘驱动器上的第一个分区从扇区 63 开始,而 MBR 在扇区 0 中,这留下了 62 个 512 字节扇区(31,744 字节)用于存储 core.img 文件,该文件是 GRUB 的阶段 1.5。 core.img 文件为 25,389 字节,因此在 MBR 和第一个磁盘分区之间有足够的空间来存储它。

由于阶段 1.5 可以容纳更多的代码,因此它可以具有足够的代码来包含一些常见的文件系统驱动程序,例如标准 EXT 和其他 Linux 文件系统、FAT 和 NTFS。 GRUB2 core.img 比旧的 GRUB1 阶段 1.5 更复杂和功能更强大。 这意味着 GRUB2 的阶段 2 可以位于标准 EXT 文件系统上,但不能位于逻辑卷上。 因此,阶段 2 文件的标准位置是 /boot 文件系统,特别是 /boot/grub2。

请注意,/boot 目录必须位于 GRUB 支持的文件系统上。 并非所有文件系统都受支持。 阶段 1.5 的功能是以文件系统驱动程序开始执行,这些驱动程序对于在 /boot 文件系统中定位阶段 2 文件并加载所需的驱动程序是必需的。

阶段 2

GRUB 阶段 2 的所有文件都位于 /boot/grub2 目录和几个子目录中。 GRUB2 没有像阶段 1 和 2 那样的映像文件。 相反,它主要由运行时内核模块组成,这些模块根据需要从 /boot/grub2/i386-pc 目录加载。

GRUB2 阶段 2 的功能是定位和加载 Linux 内核到 RAM 中,并将计算机的控制权交给内核。 内核及其关联文件位于 /boot 目录中。 内核文件是可识别的,因为它们的名称都以 vmlinuz 开头。 您可以列出 /boot 目录的内容以查看系统上当前安装的内核。

GRUB2,如 GRUB1 一样,支持从选择的 Linux 内核之一启动。 Red Hat 包管理器 DNF 支持保留内核的多个版本,以便在最新版本出现问题时,可以引导旧版本的内核。 默认情况下,GRUB 提供已安装内核的预引导菜单,包括救援选项,如果已配置,还包括恢复选项。

GRUB2 的阶段 2 将选定的内核加载到内存中,并将计算机的控制权交给内核。

内核

所有内核都采用自解压缩的压缩格式,以节省空间。 内核与初始 RAM 磁盘映像和硬盘驱动器的设备映射一起位于 /boot 目录中。

在选定的内核加载到内存中并开始执行后,它必须首先从文件的压缩版本中解压缩自身,然后才能执行任何有用的工作。 内核解压缩自身后,它加载 systemd,它是旧 SysV init 程序的替代品,并将控制权交给它。

这是引导过程的结束。 此时,Linux 内核和 systemd 正在运行,但由于没有其他程序在运行,因此无法为最终用户执行任何生产性任务。

启动过程

启动过程在引导过程之后进行,使 Linux 计算机进入可操作状态,使其可用于生产性工作。

systemd

systemd 是所有进程的母进程,它负责使 Linux 主机进入可以完成生产性工作的状态。 它的一些功能远比旧的 init 程序广泛,包括管理正在运行的 Linux 主机的许多方面,包括挂载文件系统,以及启动和管理使 Linux 主机具有生产力所需的服务。 systemd 的任何与启动序列无关的任务都不在本文的范围之内。

首先,systemd 挂载 /etc/fstab 定义的文件系统,包括任何交换文件或分区。 此时,它可以访问位于 /etc 中的配置文件,包括它自己的配置文件。 它使用其配置文件 /etc/systemd/system/default.target 来确定它应该将主机引导到哪个状态或目标。 default.target 文件只是指向真实目标文件的符号链接。 对于桌面工作站,这通常是 graphical.target,它等效于旧 SystemV init 中的 运行级别 5。 对于服务器,默认更可能是 multi-user.target,它类似于 SystemV 中的 运行级别 3emergency.target 类似于单用户模式。

请注意,目标和服务是 systemd 单元。

下面的表 1 是 systemd 目标与旧 SystemV 启动运行级别的比较。 systemd 目标别名 由 systemd 提供,用于向后兼容。 目标别名允许脚本以及许多像我这样的系统管理员使用 SystemV 命令(如 init 3)来更改运行级别。 当然,SystemV 命令被转发到 systemd 进行解释和执行。

SystemV 运行级别 systemd 目标 systemd 目标别名 描述
  halt.target   停止系统,但不关闭电源。
0 poweroff.target runlevel0.target 停止系统并关闭电源。
S emergency.target   单用户模式。 没有服务正在运行; 文件系统未挂载。 这是最基本的操作级别,只有紧急 shell 在主控制台上运行,供用户与系统交互。
1 rescue.target runlevel1.target 基本系统,包括挂载文件系统,仅运行最基本的服务,并在主控制台上运行救援 shell。
2   runlevel2.target 多用户,没有 NFS,但所有其他非 GUI 服务都在运行。
3 multi-user.target runlevel3.target 所有服务都在运行,但仅限命令行界面 (CLI)。
4   runlevel4.target 未使用。
5 graphical.target runlevel5.target 多用户,带有 GUI。
6 reboot.target runlevel6.target 重启
  default.target   此目标始终与指向 multi-user.target 或 graphical.target 的符号链接别名。 systemd 始终使用 default.target 启动系统。 default.target 永远不应别名为 halt.target、poweroff.target 或 reboot.target。

表 1:SystemV 运行级别与 systemd 目标和一些目标别名的比较。

每个目标都在其配置文件中描述了一组依赖项。 systemd 启动所需的依赖项。 这些依赖项是在特定功能级别运行 Linux 主机所需的服务。 当目标配置文件中列出的所有依赖项都已加载并运行时,系统在该目标级别运行。

systemd 还查看旧的 SystemV init 目录,以查看那里是否存在任何启动文件。 如果是这样,systemd 将这些文件用作配置文件来启动文件描述的服务。 已弃用的网络服务是仍然在 Fedora 中使用 SystemV 启动文件的示例之一。

下面的图 1 直接从 bootup 手册页 复制而来。 它显示了 systemd 启动期间的事件的一般顺序以及确保成功启动的基本排序要求。

sysinit.targetbasic.target 目标可以被视为启动过程中的检查点。 尽管 systemd 的设计目标之一是以并行方式启动系统服务,但仍然存在某些服务和功能目标必须在其他服务和目标可以启动之前启动。 在满足该检查点所需的所有服务和目标之前,这些检查点无法通过。

因此,当完成它所依赖的所有单元时,将达到 sysinit.target。 所有这些单元(挂载文件系统、设置交换文件、启动 udev、设置随机生成器种子、启动底层服务以及设置加密服务(如果一个或多个文件系统已加密))都必须完成,但在 sysinit.target 中,这些任务可以并行执行。

sysinit.target 启动系统具有边际功能所需的所有底层服务和单元,并且这些服务和单元是启用移动到 basic.target 所必需的。

   local-fs-pre.target
            |
            v
   (various mounts and   (various swap   (various cryptsetup
    fsck services...)     devices...)        devices...)       (various low-level   (various low-level
            |                  |                  |             services: udevd,     API VFS mounts:
            v                  v                  v             tmpfiles, random     mqueue, configfs,
     local-fs.target      swap.target     cryptsetup.target    seed, sysctl, ...)      debugfs, ...)
            |                  |                  |                    |                    |
            \__________________|_________________ | ___________________|____________________/
                                                 \|/
                                                  v
                                           sysinit.target
                                                  |
             ____________________________________/|\________________________________________
            /                  |                  |                    |                    \
            |                  |                  |                    |                    |
            v                  v                  |                    v                    v
        (various           (various               |                (various          rescue.service
       timers...)          paths...)              |               sockets...)               |
            |                  |                  |                    |                    v
            v                  v                  |                    v              rescue.target
      timers.target      paths.target             |             sockets.target
            |                  |                  |                    |
            v                  \_________________ | ___________________/
                                                 \|/
                                                  v
                                            basic.target
                                                  |
             ____________________________________/|                                 emergency.service
            /                  |                  |                                         |
            |                  |                  |                                         v
            v                  v                  v                                 emergency.target
        display-        (various system    (various system
    manager.service         services           services)
            |             required for            |
            |            graphical UIs)           v
            |                  |           multi-user.target
            |                  |                  |
            \_________________ | _________________/
                              \|/
                               v
                     graphical.target

图 1:systemd 启动图。

在满足 sysinit.target 后,systemd 接下来启动 basic.target,启动满足它所需的所有单元。 基本目标通过启动下一个目标所需的单元来提供一些附加功能。 这些包括设置到各种可执行目录、通信套接字和计时器的路径。

最后,可以初始化用户级目标 multi-user.targetgraphical.target。 请注意,必须先达到 multi-user.target ,然后才能满足图形目标依赖项。

图 1 中带下划线的目标是常用的启动目标。 当达到这些目标之一时,启动完成。 如果 multi-user.target 是默认值,那么您应该在控制台上看到文本模式登录。 如果 graphical.target 是默认值,那么您应该看到图形登录; 您看到的特定 GUI 登录屏幕将取决于您使用的默认 显示管理器

问题

我最近需要更改使用 GRUB2 的 Linux 计算机上的默认启动内核。 我发现某些命令似乎对我不起作用,或者我没有正确使用它们。 我还不确定是哪种情况,需要做更多的研究。

grub2-set-default 命令没有正确地为我在 /etc/default/grub 文件中设置默认内核索引,因此所需的备用内核没有启动。 因此,我手动将 /etc/default/grub GRUB_DEFAULT=saved 更改为 GRUB_DEFAULT=2,其中 2 是我要启动的已安装内核的索引。 然后,我运行命令 grub2-mkconfig > /boot/grub2/grub.cfg 以创建新的 grub 配置文件。 此规避方法按预期工作,并引导到备用内核。

结论

GRUB2 和 systemd init 系统是大多数现代 Linux 发行版的引导和启动阶段的关键组件。 尽管围绕 systemd 尤其存在争议,但这两种组件可以平稳地协同工作,首先加载内核,然后启动生成功能性 Linux 系统所需的所有系统服务。

尽管我确实发现 GRUB2 和 systemd 比其前身更复杂,但它们也同样易于学习和管理。 手册页包含大量关于 systemd 的信息,而 freedesktop.org 在线提供了完整的 systemd 手册页 集。 有关更多链接,请参阅下面的资源。

其他资源

标签
David Both
David Both 是开源软件和 GNU/Linux 的倡导者、培训师、作家和演讲者。 他自 1996 年以来一直从事 Linux 和开源软件工作,自 1969 年以来一直从事计算机工作。 他是 “系统管理员的 Linux 哲学” 的坚定支持者和传播者。

10 条评论

非常感谢您撰写关于 Linux 启动的文章! 我一直想找到这样的资源。

我从过去怀念的是旧的启动操作列表流。 我想也许这对某些人来说太可怕了,所以我们不得不使用这种图形化启动,它什么也没告诉我们。

这可以修复。 编辑 /etc/default/grub 并从内核参数行中删除 “rhgb” 和 “quiet”。 然后运行命令

grub2-mkconfig > /boot/grub2/grub.cfg

该命令使用修改后的内核参数创建新的 grub.cfg,因此您现在应该看到启动期间可用的所有有趣信息。

我发现这些信息非常有用,因为启动期间可能出现问题甚至崩溃。 我可以通过观看信息的流动来判断很多事情,听起来你也可以。

享受吧!

回复 作者 Greg P

当我修改启动过程时,我总是要深吸一口气,当系统无法启动时,这真是一件 PITA。
无论如何,我发现我需要成为实际的 root 用户而不是 sudo 才能运行此命令(我认为是因为文件的权限),而我真正需要做的是(在 F24 上)
grub2-mkconfig > /boot/grub2/grub.cfg
因为这是 grub.cfg 实际所在的位置。

我想补充一点,实际上,我发现启动消息更常提醒您注意某些不会阻止启动的事情,但可能会影响您之后需要的某些服务,并让您提前了解某些故障。

对我来说,这是一篇非常及时的文章,因为我昨天不得不深入研究所有这些!

非常感谢您的这篇文章,它很棒。

在 2017 年谈论 mbr 和 bios 似乎很奇怪,我曾希望您会介绍 gpt 和 uefi。

感谢您对启动和启动过程的精彩总结。 考虑更新以解释哪些发行版可能遵循您描述的方案。 例如,我在我的 Linux Mint 17.3 工作站上找不到任何 *.target 文件,尽管启动+启动过程使用了 “systemd”。

Creative Commons License本作品根据 Creative Commons Attribution-Share Alike 4.0 International License 获得许可。
© . All rights reserved.