Linux 启动和启动过程简介

有没有想过让您的系统准备好运行应用程序需要什么?以下是幕后发生的事情。
740 位读者喜欢这篇文章。
The boot process

企鹅,启动。由 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”(大统一引导加载程序,版本 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 目录的内容以查看系统上当前安装的内核。

与 GRUB1 一样,GRUB2 支持从选定的 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 中的 runlevel 5。对于服务器,默认更可能是 multi-user.target,它类似于 SystemV 中的 runlevel 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

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

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

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

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

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

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

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.