首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Ubuntu Server 引导过程详解:从按下电源键到系统就绪的深度之旅

Ubuntu Server 引导过程详解:从按下电源键到系统就绪的深度之旅

原创
作者头像
徐关山
发布2025-09-11 12:28:36
发布2025-09-11 12:28:36
2780
举报

前言

当我们面对一台运行着 Ubuntu Server 的生产环境服务器时,很少会去思考它是如何从冰冷的硬件变为一个提供强大网络服务的有机整体。按下电源键后的几十秒内,背后发生了一系列精密而复杂的连锁反应。理解这个过程,不仅是系统管理员和运维工程师的基本功,更是我们进行系统故障排除、性能优化和安全加固的基石。

本文将深入剖析 Ubuntu Server 的完整引导过程。我们将穿越硬件、固件、引导加载程序、内核与用户空间的层层关卡,详细探索每个阶段的任务、机制与核心组件。无论您是初学 Linux 的爱好者,还是经验丰富的系统架构师,相信都能从这篇详尽的指南中获益。

第一部分:序幕——硬件初始化与固件阶段

任何操作系统的引导都始于硬件层面。当我们按下服务器的电源按钮时,一场精心编排的启动交响曲正式拉开帷幕。

第一章:电源启动与自检

1.1 上电与电源就绪信号

按下电源按钮并非直接给整个主板供电,而是向电源供应单元(PSU)发送一个信号。PSU 随后进行自检,确保各路输出电压(+12V, +5V, +3.3V, -12V, +5VSB)都稳定在规范范围内。一旦就绪,PSU 会向主板发送一个 Power Good 信号。

1.2 CPU 复位与起点

主板上的芯片组(如 Intel 的 PCH 或 AMD 的 FCH)收到 Power Good 信号后,会释放 CPU 的复位引脚(RESET#)。CPU 结束复位状态,但其内部所有寄存器(除了代码段寄存器 CS 和指令指针寄存器 IP/EIP/RIP)都处于一个不确定的状态。最关键的是,CS:IP 寄存器对被硬连线指向一个特定的内存地址,这对于所有 x86/x86-64 CPU 都是相同的:0xFFFFFFF0。这个地址位于主板 ROM 的映射区域,也就是我们常说的 BIOS 或 UEFI 固件的入口点。

1.3 POST:开机自检

CPU 开始从固件中执行代码,第一项重大任务就是开机自检(Power-On Self-Test, POST)。POST 过程由固件代码实现,主要包括:

  • 核心硬件检测:检查 CPU、基本内存(DRAM)、中断控制器、DMA 控制器等是否存在且功能正常。
  • 硬件初始化:配置CPU、内存控制器、设置早期内存映射(如将 ROM 映射到 0xFFFFFFF0)。
  • 外围设备枚举:检测和初始化显卡、键盘、磁盘控制器等关键设备。
  • 硬件健康状态检查:通过传感器检查电压、温度、风扇转速是否在安全范围内。

如果 POST 过程中遇到关键错误(如无内存、无显卡),固件会通过蜂鸣器代码或主板诊断灯的形式报告错误,引导过程会中止。

第二章:固件接口:BIOS 与 UEFI

现代服务器主要采用 UEFI 固件,但传统 BIOS 模式仍然存在。理解两者的区别对理解引导过程至关重要。

2.1 传统 BIOS(Basic Input/Output System)

BIOS 是存在已久的标准,其工作方式较为简单:

  • 存储:BIOS 代码通常存储在主板上的一块 SPI Flash 芯片中。
  • 运行环境:BIOS 运行在 16 位的实模式(Real Mode)下,内存寻址空间被限制在 1MB。
  • 引导机制:BIOS 的引导依赖主引导记录(MBR)。它会按照预设的启动顺序(Boot Order)遍历每个存储设备,读取每个设备的第一个扇区(512 字节),即 MBR。MBR 的最后两个字节必须是固定的魔数 0x55AA,否则 BIOS 会认为该设备不可引导,转而尝试下一个设备。
  • 局限性
    • 无法识别大于 2.2TB 的磁盘(MBR 分区表限制)。
    • 引导代码极其有限(只有 440 字节的引导程序代码+64字节的分区表+2字节魔数)。
    • 缺乏预引导环境,驱动模型简陋。

2.2 UEFI(Unified Extensible Firmware Interface)

UEFI 是现代固件接口,旨在克服 BIOS 的诸多限制:

  • 存储:UEFI 固件同样存储在 SPI Flash 中,但通常更大更复杂。
  • 运行环境:UEFI 应用程序运行在 32 位或 64 位的保护模式(Protected Mode)下,可以访问所有内存和硬件资源。它自带强大的服务(Boot Services 和 Runtime Services),如内存管理、协议接口、驱动等。
  • 引导机制:UEFI 不再依赖 MBR 的引导代码。它理解分区表文件系统。UEFI 固件会查找一个特定的EFI 系统分区(ESP)。该分区通常格式化为 FAT32/VFAT 文件系统,其中存放着扩展名为 .efi 的引导程序文件(如 grubx64.efi)。UEFI 固件直接加载并执行这个 .efi 应用程序。
  • 优势
    • 支持 GPT 分区表,突破 2.2TB 磁盘限制。
    • 更快的启动速度(并行初始化硬件、驱动化)。
    • 安全启动(Secure Boot)功能,防止加载未签名的恶意代码。
    • 强大的预引导 shell 环境和诊断工具。
    • 模块化设计,易于扩展。

2.3 UEFI 启动流程详解

UEFI 的启动过程比 BIOS 更为结构化:

  1. SEC(Security Phase):第一个阶段,负责接收和处理 Power Good 信号,初始化临时内存(Cache as RAM),验证后续阶段代码的完整性。
  2. PEI(Pre-EFI Initialization):初始化永久内存(DRAM)和足够多的硬件,以便为 DXE 阶段准备好执行环境。这是一个资源非常受限的环境。
  3. DXE(Driver Execution Environment):核心阶段。DXE 基金会(Foundation)被加载,它随后加载和执行大量的 DXE 驱动程序。这些驱动程序以模块化方式初始化所有硬件设备(CPU、芯片组、存储、网络等),并发布相应的协议(Protocols)接口。
  4. BS(Boot Services):在 DXE 末期,引导服务表(Boot Services Table)已经就绪。它提供了内存管理、协议处理、事件定时器等服务。
  5. TSL(Transient System Load):选择启动设备,加载并执行 EFI 应用程序(如引导管理器)。
  6. RT(Runtime):操作系统加载器调用 ExitBootServices(),告知固件它已经准备好接管系统。此后,只有运行时服务(如获取时间、NVRAM 访问)仍然可用,引导服务被完全禁用。
  7. AL(After Life):操作系统正常运行。

UEFI 固件通过启动管理器(Boot Manager)来选择要加载的 EFI 应用程序。启动配置数据存储在 NVRAM 中,可以通过 efibootmgr 命令在 Linux 中查看和修改。

代码语言:txt
复制
# 示例:查看 UEFI 启动项
$ sudo efibootmgr -v
BootCurrent: 0000
Timeout: 1 seconds
BootOrder: 0000,0001,0002
Boot0000* ubuntu    HD(1,GPT,12345678-1234-1234-1234-123456789abc,0x800,0x100000)/File(\EFI\UBUNTU\SHIMX64.EFI)
Boot0001* UEFI: CD/DVD Drive    BBS(129,,0x0)
Boot0002* UEFI: Network Card    BBS(130,,0x0)

第二部分:引导加载程序阶段——GRUB2 的舞台

固件找到了引导程序并将其加载到内存后,控制权就移交给了引导加载程序(Bootloader)。在 Ubuntu Server 中,这个角色几乎毫无例外地由 GRUB2(GRand Unified Bootloader, version 2) 扮演。

第三章:GRUB2 概述与架构

GRUB2 是一个强大、模块化、跨平台的引导程序,是早期 GRUB Legacy 的彻底重写。它的核心任务是:

  1. 加载 Linux 内核映像(vmlinuz)到内存。
  2. 加载初始内存磁盘(initrd/initramfs)到内存。
  3. 为内核传递启动参数(如根文件系统位置 root=)。
  4. 提供一个菜单界面,允许用户选择不同的内核或操作系统。

3.1 GRUB2 的模块化设计

GRUB2 本身非常精简。它的核心镜像文件(core.img.efi 文件)只包含最基本的功能:设备驱动、文件系统驱动、环境变量处理等都是以模块(.mod) 的形式存在,按需动态加载。这种设计使得 GRUB2 可以保持小巧,同时支持海量的硬件和文件系统。

3.2 GRUB2 的安装位置

  • BIOS/MBR 模式:
    • boot.img: 安装在 MBR 的前 440 字节。它的唯一作用是从本地磁盘加载紧接着 MBR 的 core.img
    • core.img: 安装在 MBR 之后的扇区(通常称为 "MBR gap")。它包含了足够的代码来访问 /boot/grub 目录,并加载其中的模块。由于 core.img 较大,它可能会侵占第一个分部的空间,这也是为什么通常建议在磁盘开头留出一个未分区的空间。
    • /boot/grub: 包含所有的模块、配置文件 (grub.cfg)、主题等。
  • UEFI/GPT 模式:
    • grubx64.efi (或 shimx64.efi): 这是一个独立的 EFI 应用程序,存放在 EFI 系统分区 (ESP) 的 \EFI\ubuntu\ 目录下。它已经将 core.img 和必要的模块静态链接在一起,因此可以直接由 UEFI 固件加载执行。之后,它会读取 ESP 或其它分区上的 /boot/grub 目录来加载更多模块和配置。

第四章:GRUB2 的启动流程解析

GRUB2 的启动是一个多阶段的过程,尤其在 BIOS 模式下更为明显。

4.1 BIOS/MBR 模式下的阶段

  1. Stage 1: boot.img
    • BIOS 将 MBR 的 512 字节加载到内存 0x7C00 并执行。
    • boot.img 的唯一任务是加载 core.img 的第一个扇区。它通过 BIOS 中断调用(INT 13h)直接读取硬盘,因为它还不理解文件系统。
    • boot.img 的大小被严格限制在 440 字节。
  2. Stage 1.5: core.img
    • core.img 通常由 diskboot.imgkernel.img 以及一些核心模块(如 biosdisk.mod, part_msdos.mod, ext2.mod) 拼接而成。
    • diskboot.img 负责加载 core.img 的剩余部分。
    • 现在,GRUB2 拥有了访问磁盘和解析基本文件系统(如 ext2/3/4)的能力。
    • core.img 最终的任务是加载并执行 kernel.img,并将控制权交给它。
  3. Stage 2: /boot/grub 目录
    • kernel.img 是 GRUB2 的“内核”,它初始化控制台、动态内存管理(堆)、设备驱动、模块加载器等。
    • 它随后从 /boot/grub 目录加载 normal.mod 模块。
    • normal.mod 执行 normal 命令,该命令读取并解析 /boot/grub/grub.cfg 配置文件。
    • 根据 grub.cfg 的配置,GRUB2 会呈现一个漂亮的启动菜单,并等待用户交互或超时。

4.2 UEFI 模式下的简化流程

UEFI 模式大大简化了这个过程:

  1. 固件直接加载 grubx64.efi(它是一个完整的 PE/COFF 格式的可执行文件)到内存并执行。
  2. grubx64.efi 内部已经包含了相当于 core.img 的功能和核心模块。
  3. 它直接从 EFI 系统分区(或配置指定的其他分区)读取 /boot/grub/grub.cfg 文件。
  4. 后续流程与 BIOS 模式的 Stage 2 相同。

第五章:深入 grub.cfg 与内核加载

/boot/grub/grub.cfg 是 GRUB2 的核心配置文件,但它通常不是由管理员直接编辑的,而是由 update-grub 命令(背后是 grub-mkconfig)根据 /etc/default/grub/etc/grub.d/ 目录下的脚本自动生成的。

5.1 grub.cfg 结构剖析

一个典型的 grub.cfg 包含以下部分:

  • 菜单项(Menuentries):每个菜单项对应一个可以启动的操作系统或内核。menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class gnu --class os $menuentry_id_option 'gnulinux-simple-12345678-1234-1234-1234-123456789abc' { recordfail load_video gfxmode $linux_gfx_mode insmod gzio insmod part_gpt insmod ext2 set root='hd0,gpt2' if [ x$feature_platform_search_hint = xy ]; then search --no-floppy --fs-uuid --set=root --hint-bios=hd0,gpt2 --hint-efi=hd0,gpt2 --hint-baremetal=ahci0,gpt2 12345678-1234-1234-1234-123456789abc else search --no-floppy --fs-uuid --set=root 12345678-1234-1234-1234-123456789abc fi linux /boot/vmlinuz-5.4.0-42-generic root=UUID=12345678-1234-1234-1234-123456789abc ro quiet splash $vt_handoff initrd /boot/initrd.img-5.4.0-42-generic }
    • menuentry: 定义了一个启动菜单项。
    • insmod: 加载所需的模块(如文件系统 ext2、压缩 gzio)。
    • set root: 设置根设备,指定 (hd0, gpt2) 表示第一个硬盘的第二个 GPT 分区。
    • search: 更智能地搜索包含指定 UUID 的分区并设置为根设备。
    • linux: 最关键的命令。指定要加载的 Linux 内核映像文件路径,并传递内核启动参数
      • root=UUID=...: 告诉内核根文件系统所在分区的 UUID。
      • ro: 以只读模式挂载根文件系统(初始阶段出于安全考虑)。
      • quiet splash: 控制内核启动时的输出级别和是否显示闪烁标志。
    • initrd: 指定要加载的初始内存磁盘(initramfs)映像文件路径。

5.2 内核加载的底层细节

当用户选择一个菜单项后,GRUB2 执行 linuxinitrd 命令:

  1. 加载内核 (linux 命令):
    • GRUB2 使用其文件系统驱动找到 vmlinuz 文件。
    • 它将这个压缩的内核映像加载到内存中的一个特定地址。内核映像是一个 bzImage(big zImage),它内部包含了一段实模式代码和解压缩程序。
    • GRUB2 还会在内存中构建一个引导参数列表,其结构体形式为 boot_params(对于 x86架构),其中包含了从 grub.cfg 中解析来的命令行参数、内存映射图(e820 map)、硬件信息等。
  2. 加载 initramfs (initrd 命令):
    • GRUB2 同样将 initrd.img 文件加载到内存中的另一块区域。
    • initrd.img 是一个 CPIO 归档文件,通常还用 gzip 压缩过。它的内容将在后续阶段被内核解压并挂载。
  3. 移交控制权:
    • 一切准备就绪后,GRUB2 调用其 boot 命令(或超时自动执行)。
    • GRUB2 清理现场,然后跳转到已加载的内核映像的入口点,将控制权彻底移交给 Linux 内核。同时,它还会将 boot_params 结构体的地址传递给内核。

第三部分:内核初始化阶段——Linux 的崛起

现在,CPU 开始执行 Linux 内核的代码。这是从“引导”到“操作系统”的转折点。内核需要解压自己,初始化关键子系统,并最终切换到真正的根文件系统。

第六章:实模式内核的初始化

内核映像的入口点是一小段实模式(16位)代码。

  1. 环境设置:这段代码的主要任务是确保 CPU 处于一个已知的状态:设置栈、中断描述符表(IDT)、全局描述符表(GDT),并启用 A20 地址线(为了访问 1MB 以上的内存,这是一个历史遗留问题)。
  2. 解压内核:调用内置的解压程序(通常是 gunzip),将压缩的内核代码(位于高位内存)解压到低位内存的特定地址(通常是 0x100000,即 1MB 处)。这就是所谓的 "解压内核 above, 放置内核 below"
  3. 进入保护模式:解压完成后,代码会切换到 32 位保护模式,为执行真正的高阶内核代码做好准备。
  4. 跳转到保护模式入口点:最后,它跳转到解压后的 32 位内核的起始地址。

第七章:高阶内核初始化 (startup_32start_kernel)

解压后的内核代码开始执行,其初始化过程是一个从架构相关代码逐步过渡到架构无关代码的旅程。

7.1 架构相关初始化 (startup_32)

在 x86 上,这是 arch/x86/boot/compressed/head_64.S 中的 startup_32(对于 32 位内核)或 startup_64(对于 64 位内核)。主要任务:

  • 最终环境准备:彻底完成保护模式/长模式的切换,设置分页(Paging),建立最终的内存映射。
  • 重定位:可能需要将内核移动到其编译时预期的最终虚拟地址(对于 x86_64 Linux,通常是 0xffffffff81000000)。
  • 解析 Boot Parameters:从 GRUB2 传递过来的 boot_params 结构体中提取信息,如内存布局、命令行参数等。
  • 调用架构无关的主入口:最终,它调用架构无关的 Linux 内核主入口函数:start_kernel()

7.2 架构无关初始化 (start_kernel)

init/main.c 中的 start_kernel() 函数是所有 Linux 架构的共通入口点。它是一个巨大的函数,按顺序初始化内核的几乎所有子系统:

  • 初始化锁机制、调度器trap_init(), sched_init()
  • 设置内存管理mm_init():初始化伙伴系统分配器(Buddy System),这是物理内存管理的核心。
  • 初始化调度器sched_init()
  • 解析启动命令parse_early_param(), parse_args():解析从 GRUB 传来的 root=, ro, quiet 等参数。
  • 初始化控制台console_init():此时内核才能通过 printk 输出信息到屏幕。
  • 初始化进程管理fork_init()
  • 初始化虚拟文件系统(VFS)vfs_caches_init()
  • 调用架构的 rest_init:在 start_kernel 的最后,它调用 rest_init(),标志着核心初始化工作的完成。

7.3 rest_init() 与第一个进程

rest_init() 的工作至关重要:

  1. 创建内核线程 kernel_init:这个线程的任务就是启动用户空间的第一个进程。但在那之前,它必须先处理 initramfs
  2. 创建内核线程 kthreadd:这是所有其它内核线程的父进程(pid=2)。
  3. start_kernel 返回start_kernel 函数不会返回。执行 rest_init 的上下文(可以看作是第 0 号进程,即 idle 进程)最终会调用 cpu_startup_entry(CPUHP_ONLINE),进入 idle 循环,当系统中没有其它任务可运行时,CPU 就执行它的 idle 函数。

此时,调度器已经开始工作,kernel_init 线程(pid=1)被调度运行。

第八章:initramfs 的解压与执行

kernel_init 线程的首要任务是处理 initramfs

8.1 为什么需要 initramfs?

在现代 Linux 系统中,根文件系统(/)可能位于复杂的存储设备上:

  • 软件 RAID (mdadm)
  • LVM 逻辑卷
  • 加密的设备 (LUKS)
  • 网络块设备 (iSCSI, NBD)
  • 非标准文件系统 (Btrfs, ZFS)

这些设备的驱动可能没有直接编译进内核,而是以内核模块的形式存在。而内核模块本身又存储在根文件系统的 /lib/modules/ 目录下。这就形成了一个“先有鸡还是先有蛋”的悖论:需要访问根文件系统来加载模块,但又需要模块来访问根文件系统

initramfs (Initial RAM File System) 就是为了解决这个悖论而生的。它是一个临时的根文件系统,被加载到内存中。它包含了在挂载真实根文件系统之前所必需的工具、脚本和内核模块。

8.2 initramfs 的组成与解压

  • 格式initrd.img 是一个 gzip 压缩的 CPIO 归档文件。CPIO 是一种简单的文件归档格式。
  • 解压:内核在初始化早期(populate_rootfs 函数)就会将其解压到一个基于 tmpfs 的内存文件系统中,并将其挂载为最初的根文件系统(/)。

8.3 /init 脚本——initramfs 的灵魂

解压 initramfs 后,内核会在其根目录下寻找一个名为 /init 的文件(如果不存在,则寻找 /linuxrc)。这个文件必须是可执行的,它将成为用户空间的第一个进程(pid=1 的进程,由 kernel_init 线程exec执行)。

在 Ubuntu Server 的 initramfs 中,/init 是一个复杂的 Shell 脚本(通常是 initramfs-tools 包生成的),它的核心使命是:

  1. 初始化基本设备:如 /dev, /proc, /sys
  2. 加载关键内核模块:如磁盘控制器、文件系统、加密等模块。这些模块通常被硬编码在 initramfs/conf/ 目录下,或通过 lvm2, cryptsetup 等工具动态探测。
  3. 发现根设备:根据 grub.cfg 中传递的 root=UUID=... 参数,找到对应的物理设备。这可能涉及解密(cryptsetup)、激活 LVM 卷组(vgchange)、组装 RAID 阵列等。
  4. 以只读方式挂载真正的根文件系统:到 initramfs 下的一个目录(如 /root)。
  5. 清理并切换根目录
    • 调用 pivot_root 系统调用,将真正的根文件系统 (/root) 切换为新的根 (/)。
    • 卸载旧的 initramfs 根文件系统并释放内存。
  6. 执行真正的 init 程序:最后,/init 脚本通过 exec 系统调用,执行真实根文件系统上的 /sbin/init(通常是 systemdupstart,在现代 Ubuntu 中是 systemd)。至此,/sbin/init 替代了 initramfs/init,成为新的 pid=1 进程。

8.4 调试 initramfs

如果系统卡在 initramfs 阶段,通常会掉入一个 BusyBox 提供的救急 shell。这时可以使用以下命令进行调试:

  • ls /dev/disk/by-uuid/: 查看所有设备的 UUID。
  • blkid: 查看块设备信息。
  • cryptsetup luksOpen /dev/sda5 myroot: 打开加密的根设备。
  • lvm vgchange -ay: 激活 LVM 卷组。
  • mount /dev/mapper/ubuntu--vg-root /root: 尝试挂载根文件系统。
  • exit: 退出 shell,继续启动过程(有时会有效)。

第四部分:用户空间初始化——systemd 的统治

控制权从内核移交到用户空间的第一个进程 /sbin/init,这标志着用户空间初始化的开始。在现代 Ubuntu Server 中,/sbin/init 是指向 /lib/systemd/systemd 的符号链接。systemd 就此接管系统,成为所有进程的父进程(pid=1)。

第九章:systemd 概述与设计哲学

systemd 不仅仅是一个 init 程序,它是一个庞大的系统和服务管理器套件。其设计目标包括:

  • 提高启动速度:通过并行启动服务。
  • 明确的依赖关系:基于单元(Unit)文件明确定义服务间的依赖,而不是传统的启动序号(SYSVinit)。
  • 基于事务的启动:如果某个服务启动失败,其依赖者也不会启动。
  • 统一的管理接口:使用 systemctl 命令统一管理所有系统服务。
  • 集成日志:通过 journald 收集所有内核和用户空间的日志。

第十章:systemd 的启动流程详解

systemd 的启动过程是一个按目标(target)推进的链条。目标可以看作是运行级别(runlevel)的现代化替代品,但它更灵活,可以并行化。

10.1 初始目标:default.target

systemd 启动后,它的核心任务是到达默认目标(default.target)。这通常是一个符号链接,指向 graphical.target(桌面版)或 multi-user.target(服务器版)。

代码语言:txt
复制
# 查看默认目标
$ systemctl get-default
multi-user.target

10.2 启动链条与目标依赖

systemd 通过目标的依赖关系来构建一个启动链条。你可以使用 systemctl list-dependencies 来查看一个目标的依赖树。

代码语言:txt
复制
$ systemctl list-dependencies multi-user.target
multi-user.target
● ├─acpid.path
● ├─atd.service
● ├─cron.service
● ├─dbus.service
● ├─getty.target
● │ ├─getty@tty1.service
● │ ├─...
● ├─remote-fs.target
● │ ├─nfs-client.target
● │ │ └─nfs-blabla.service
● │ └─rpc_pipefs.target
● ├─systemd-ask-password-wall.path
● ├─systemd-logind.service
● ├─systemd-user-sessions.service
● ├─ufw.service
● └─...

这个依赖树展示了 multi-user.target 所依赖的所有其他目标和服务。systemd 会解析这些依赖,并最大限度地并行启动它们。

10.3 关键的启动阶段(Targets)

启动过程会依次经过几个关键的目标:

  1. local-fs-pre.target: 挂载本地文件系统之前的准备工作。
  2. local-fs.target: 挂载所有本地文件系统(除了 /home 和用户相关的)。/etc/fstab 中列出的文件系统会在这里被挂载。systemd 会生成对应的 .mount 单元来自动处理 fstab
  3. swap.target: 激活所有交换分区。
  4. cryptsetup.target: 等待任何加密设备设置完成(如果已经在 initramfs 中解密,则很快通过)。
  5. remote-fs.target: 挂载网络文件系统(NFS)。
  6. systemd-udevd.service: 用户空间设备管理器。它处理所有内核通过 uevent 发出的事件(如设备插拔),加载相应的内核模块,并创建设备节点。它很早就被启动。
  7. sysinit.target: 一个重要的目标,它代表“系统初始化”完成。它依赖于所有基础的系统组件,如 local-fs.target, swap.target, udev 等。
  8. basic.target: 在 sysinit.target 之后启动,代表“基本系统”就绪。
  9. multi-user.target: 最终目标之一。所有系统服务(如 SSH, Cron, NTP, Web Server)都在这个目标下启动。系统进入多用户模式,但无图形界面。
  10. graphical.target: 在 multi-user.target 的基础上启动图形界面(如果安装了)。

10.4 服务(Service)单元的启动

每个服务(如 ssh.service, nginx.service)都是一个单元文件(.service),存放在 /lib/systemd/system//etc/systemd/system/ 中。systemd 根据依赖关系和并行化原则启动它们。

一个服务单元文件定义了如何启动、停止和管理该服务:

代码语言:ini
复制
# 示例:简化的 ssh.service
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run

[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify

[Install]
WantedBy=multi-user.target
Alias=sshd.service
  • [Unit] 段:描述服务和依赖关系。After= 表示本服务在 network.target 之后启动。
  • [Service] 段:定义执行参数。ExecStart= 是启动服务的命令。
  • [Install] 段:定义如何“安装”这个服务,即当用户 systemctl enable ssh 时,该服务会被添加到 multi-user.targetWants 列表中,从而在启动时自动运行。

第十一章:getty 与用户登录

当系统服务基本就绪后,getty.target 会被激活。它启动一系列 getty@tty1.service 之类的服务。

  • getty (get teletype):它的作用是管理物理或虚拟终端(tty)。它会在终端上显示 login: 提示符。
  • login:当用户输入用户名后,getty 会 exec 到 login 程序,由它来验证密码。
  • shell:密码验证成功后,login 程序会根据 /etc/passwd 的配置,启动用户的登录 shell(如 /bin/bash)。
  • $SHELL:bash 启动后,它会读取用户的 .bashrc 和系统级的 /etc/profile 等初始化脚本。最终,用户获得一个可交互的命令行提示符。

对于服务器,SSH 服务(ssh.service 是更常见的登录方式。它的工作原理与 getty 类似,但通过网络进行认证和会话建立。

第五部分:高级主题与故障排除

第十二章:Secure Boot 与 Shim

在 UEFI 安全启动(Secure Boot) enabled 的系统中,固件只会加载被数字签名的 EFI 应用程序。由于 GRUB2 和 Linux 内核是开源软件,频繁变化,让微软为其签名是不现实的。

解决方案是使用一个中间人shim

  1. shim:一个很小、很简单、并且已经由微软签名的 EFI 应用程序(shimx64.efi)。它的唯一使命是加载另一个 EFI 应用程序,但会先验证其签名。不过,它信任由 Canonical(Ubuntu 母公司)签名的证书。
  2. GRUB2:Ubuntu 的 GRUB2 (grubx64.efi) 由 Canonical 签名。shim 验证其签名后,会加载它。
  3. 内核:GRUB2 也可以配置为验证内核的签名(虽然通常不强制),或者由内核本身的安全启动机制处理。

这样,就构成了一条信任链:微软 -> Canonical -> GRUB2 -> Linux Kernel。

第十三章:引导故障排除与修复

理解引导过程是排除故障的关键。以下是常见问题及解决思路:

13.1 GRUB2 问题

  • 现象:黑屏,提示 "no bootable device", "GRUB rescue>"。
  • 原因:MBR/core.img 损坏,或 grub.cfg 丢失/配置错误。
  • 修复:使用 Ubuntu 安装盘启动,进入 "Try Ubuntu" 模式,然后:# 挂载根文件系统 sudo mount /dev/sda1 /mnt # 挂载其他必要的文件系统(如 /boot, /dev, /proc, /sys) sudo mount /dev/sda5 /mnt/boot # 如果 /boot 是单独分区 sudo mount --bind /dev /mnt/dev sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys # chroot 到目标系统 sudo chroot /mnt # 重新安装和配置 GRUB grub-install /dev/sda update-grub

13.2 内核 Panic

  • 现象:内核打印错误信息后停止,提示 "Kernel panic - not syncing: ..."。
  • 原因:内核自身bug、硬件故障、initramfs 损坏或丢失、找不到根设备、无法执行 /init
  • 修复
    • 在 GRUB 菜单中,选择上一个能工作的内核版本启动。
    • 检查 root=UUID= 参数是否正确。
    • 重新生成 initramfs: sudo update-initramfs -u -k all

13.3 systemd 目标失败

  • 现象:系统启动后,某些服务未能成功启动,或者无法到达多用户目标。
  • 修复
    • 使用 systemctl status <service-name> 查看失败服务的状态和日志。
    • 使用 journalctl -xe 查看详细的系统日志。
    • 使用 systemctl isolate multi-user.target 尝试切换到多用户目标,看是否有更多错误信息。
    • 使用 systemctl defaultsystemctl rescue 进入救援模式进行修复。

13.4 文件系统错误

  • 现象:启动时卡住,显示 "fsck" 相关错误,或根文件系统被以只读方式挂载。
  • 修复:在 GRUB 菜单中,在内核参数后添加 init=/bin/bash 进入单用户 shell,然后运行 fsck -y /dev/sda1(根据实际情况调整设备名)。

第十四章:引导性能优化

  • 分析启动时间:使用 systemd-analyze 系列命令。systemd-analyze time # 显示总启动时间 systemd-analyze blame # 显示每个服务的启动耗时 systemd-analyze critical-chain # 显示关键路径上的服务耗时 systemd-analyze plot > boot.svg # 生成详细的启动时序图
  • 优化策略
    • 禁用不必要的服务sudo systemctl disable <service-name>
    • 内核模块黑名单:将不需要的模块添加到 /etc/modprobe.d/blacklist.conf
    • 使用初始化内存盘:确保 initramfs 只包含必需的模块和工具,避免臃肿。
    • 调整文件系统检查:对于大型分区,将 /etc/fstab 中的 pass 值设为 0,避免不必要的 fsck。
    • 使用更快的引导加载程序:如 systemd-boot(仅限 UEFI),它比 GRUB2 更轻量。

第六部分:总结

Ubuntu Server 的引导过程是一场从硬件到软件、从底层到高层的精妙舞蹈。它经历了:

  1. 固件阶段 (UEFI/BIOS):硬件初始化,寻找并加载引导程序。
  2. 引导加载程序阶段 (GRUB2):提供菜单,加载内核和 initramfs,传递参数。
  3. 内核阶段:解压自身,初始化系统核心,解压并执行 initramfs 的 /init
  4. initramfs 阶段:搭建临时根环境,加载驱动,发现、解密、挂载真正的根文件系统,并 pivot_root。
  5. 用户空间初始化阶段 (systemd):挂载文件系统,启动系统服务,管理依赖,最终提供用户登录环境。

每一个环节都承上启下,不可或缺。深入理解这个过程,不仅能让我们在系统出现问题时从容应对、精准定位,更能让我们从整体上把握 Linux 操作系统的运作机理,从而更好地进行系统定制、优化和安全加固。这台无声的交响曲,每天都在全世界的服务器上奏响,支撑着现代数字世界的运转。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 第一部分:序幕——硬件初始化与固件阶段
    • 第一章:电源启动与自检
    • 第二章:固件接口:BIOS 与 UEFI
  • 第二部分:引导加载程序阶段——GRUB2 的舞台
    • 第三章:GRUB2 概述与架构
    • 第四章:GRUB2 的启动流程解析
    • 第五章:深入 grub.cfg 与内核加载
  • 第三部分:内核初始化阶段——Linux 的崛起
    • 第六章:实模式内核的初始化
    • 第七章:高阶内核初始化 (startup_32 和 start_kernel)
    • 第八章:initramfs 的解压与执行
  • 第四部分:用户空间初始化——systemd 的统治
    • 第九章:systemd 概述与设计哲学
    • 第十章:systemd 的启动流程详解
    • 第十一章:getty 与用户登录
  • 第五部分:高级主题与故障排除
    • 第十二章:Secure Boot 与 Shim
    • 第十三章:引导故障排除与修复
    • 第十四章:引导性能优化
  • 第六部分:总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档