// Linux · 2026-06-24

initramfs 技术全景解析:从启动机制到实战构建

一、引言:为什么需要 initramfs?

在 Linux 系统启动过程中,存在一个经典的“鸡生蛋,蛋生鸡”悖论:内核需要文件系统驱动才能挂载根分区,但文件系统驱动本身又存放在根文件系统中。initramfs(Initial RAM Filesystem)正是为解决这一悖论而设计的临时根文件系统机制。

自 Linux 2.6 内核引入以来,initramfs 已成为现代 Linux 发行版的标准启动组件。它以内置 cpio 归档的形式存在,在启动早期提供一个最小化的用户空间环境,负责加载必要的驱动模块、解密加密分区、激活 LVM/RAID 阵列,最终挂载真实的根文件系统并将控制权移交给 init 进程。


二、Linux 启动流程中的 initramfs

2.1 启动全景图

graph TD
    A[BIOS/UEFI 加电自检 POST] --> B[Bootloader 加载阶段]
    B --> C[GRUB2 加载内核 vmlinuz]
    B --> D[GRUB2 加载 initramfs.img]
    C --> E[内核初始化 start_kernel]
    D --> F[内核解压 cpio 归档到 tmpfs]
    E --> F
    F --> G[执行 /init 脚本 PID=1]
    G --> H{是否需要加载额外驱动?}
    H -->|是| I[加载磁盘/RAID/LVM/加密驱动]
    H -->|否| J[直接挂载根文件系统]
    I --> J
    J --> K[mount 真实根分区 /dev/xxx]
    K --> L[switch_root 切换根目录]
    L --> M[执行 /sbin/init 或 systemd]
    M --> N[用户空间服务启动]
    
    style F fill:#f9f,stroke:#333,stroke-width:2px
    style G fill:#f9f,stroke:#333,stroke-width:2px
    style L fill:#f9f,stroke:#333,stroke-width:2px

2.2 内核加载 initramfs 的详细流程

当 Bootloader(如 GRUB2)将内核与 initramfs 加载到内存后,内核执行以下关键步骤:

第一阶段:内核自检与初始化

  • 内核从 start_kernel() 开始执行,完成内存管理、进程调度、中断控制等核心子系统初始化
  • 调用 rest_init() 创建第一个用户态进程 kernel_init()

第二阶段:initramfs 解压与挂载

  • 内核检测到 initramfs 存在后,创建 tmpfs 或 ramfs 内存文件系统
  • 使用内置的 cpio 解压器将归档内容释放到 rootfs 中
  • 内核检查 rootfs 中是否存在 /init 可执行文件,若存在则执行它作为 PID 1 进程

第三阶段:早期用户空间初始化

  • /init 脚本(通常为 shell 脚本)接管控制权
  • 挂载 /proc、/sys、/dev 等虚拟文件系统
  • 根据内核参数(如 root=、rd.luks.uuid=)执行相应初始化逻辑
  • 加载必要的内核模块(存储驱动、文件系统驱动、网络驱动等)

第四阶段:根文件系统切换

  • 使用 switch_root 或 pivot_root 将真实根分区挂载到 /
  • 执行 exec /sbin/init 启动 systemd 或传统 SysV init
  • initramfs 的内存空间被释放或保留作为应急环境

三、initramfs 的核心技术原理

3.1 为什么采用 cpio 而非 tar?

Linux 内核选择 cpio 作为 initramfs 的归档格式,这一决策始于 2001 年 12 月的内核邮件列表讨论。核心原因包括:

  1. 格式简洁性:cpio 的 “newc” 格式比 tar 的数十种变体更简单,内核解压代码仅约 26KB
  2. 标准兼容性:cpio 是 AT&T 时代的 Unix 标准,已被 RPM 等工具广泛使用
  3. 内核自包含:内核内置 cpio 解压器,无需依赖外部工具即可在启动早期解压
  4. Al Viro 的决策:内核开发者 Al Viro 明确表示 “tar is ugly as hell and not going to be supported on the kernel side”

3.2 cpio 归档格式详解

initramfs 使用 “newc” ASCII CPIO 格式,每个文件条目包含:

  • 110 字节 ASCII 头部:包含魔数 070701(或带 CRC 的 070702)、文件模式、UID/GID、时间戳、文件名长度、文件大小等
  • NUL 终止的文件名:以空字符结尾的路径名
  • 4 字节对齐的文件数据:数据段按 4 字节边界填充
  • TRAILER!!! 结束标记:标识归档结束
┌─────────────────┬─────────────────┬─────────────────┐
│  110B Header    │  Filename+NUL   │  Padded Data    │
│  (magic, mode,  │  (4-byte align) │  (4-byte align) │
│   size, etc.)   │                 │                 │
└─────────────────┴─────────────────┴─────────────────┘

3.3 内存文件系统:ramfs vs tmpfs

initramfs 基于内存文件系统实现,有两种底层技术:

特性 ramfs tmpfs
大小限制 无限制(可能耗尽内存) 可配置上限,支持 swap
内存使用 仅使用 page cache 使用 page cache + shmem
数据持久性 纯内存,重启丢失 纯内存,重启丢失
内核版本 早期 2.6 使用 现代内核默认使用

内核将 cpio 归档解压到 rootfs(ramfs 的特殊实例)中,文件数据直接存放在 page cache 中,避免了传统块设备的“双缓冲”问题。


四、initramfs 与 initrd 的关键区别

4.1 技术架构对比

graph BT
    subgraph "initrd (旧机制, Linux 2.4)"
        A1[ramdisk 块设备] --> B1[ext2 文件系统镜像]
        B1 --> C1[需内核块设备驱动]
        C1 --> D1[固定大小分配]
        D1 --> E1[pivot_root 切换]
        E1 --> F1[卸载 ramdisk]
    end
    
    subgraph "initramfs (现代机制, Linux 2.6+)"
        A2[ramfs/tmpfs 内存文件系统] --> B2[cpio 归档格式]
        B2 --> C2[内核原生解压支持]
        C2 --> D2[动态大小按需分配]
        D2 --> E2[switch_root 切换]
        E2 --> F2[无法卸载,直接覆盖]
    end
    
    style A2 fill:#9f9,stroke:#333,stroke-width:2px
    style B2 fill:#9f9,stroke:#333,stroke-width:2px

4.2 核心差异总结

对比维度 initrd (Initial RAM Disk) initramfs (Initial RAM Filesystem)
底层机制 基于 ramdisk 的块设备 基于 ramfs/tmpfs 的内存文件系统
文件格式 ext2 等文件系统镜像 cpio 归档(可压缩)
内核依赖 需要块设备驱动和文件系统驱动 仅需内置 cpio 解压器
内存效率 存在双缓冲(块缓存+页缓存) 直接利用页缓存,无额外开销
大小特性 创建时固定,无法动态调整 按需分配,动态增长/收缩
启动脚本 /linuxrc /init
根切换方式 pivot_root + 卸载 ramdisk switch_root 覆盖 rootfs
与内核关系 独立文件,通过 bootloader 加载 可嵌入内核镜像(usr/ 目录)
现代状态 已淘汰 所有主流发行版标准方案

值得注意的是,尽管技术已全面转向 initramfs,许多发行版仍保留 initrd.img 的命名习惯(如 CentOS 的 initramfs-$(uname -r).img、Ubuntu 的 initrd.img-$(uname -r)),这仅是历史遗留的命名约定。


五、initramfs 的内部结构解析

5.1 标准目录布局

graph TD
    subgraph "initramfs 内存文件系统 tmpfs"
        A[/init
启动脚本/] --> B[/bin/
busybox, sh, mount/]
        A --> C[/sbin/
switch_root, modprobe/]
        A --> D[/lib/
内核模块 .ko
共享库/]
        A --> E[/etc/
配置文件/]
        A --> F[/dev/
设备节点
console, null/]
        A --> G[/proc/
虚拟文件系统/]
        A --> H[/sys/
虚拟文件系统/]
        A --> I[/mnt/root/
真实根挂载点/]
    end
    
    style A fill:#ff6,stroke:#333,stroke-width:2px

5.2 关键组件说明

  • /init:核心启动脚本,由内核直接执行。负责整个早期用户空间的初始化流程,最终必须调用 switch_root 切换到真实根文件系统。若不存在,内核将回退到传统方式直接挂载根分区。

  • /bin/ 与 /sbin/:包含最小化的用户态工具集。现代发行版通常集成 BusyBox(提供 300+ 个 Unix 工具的单个二进制文件),或包含静态链接的 sh、mount、modprobe 等关键命令。

  • /lib/modules/:存放启动必需的 .ko 内核模块,如存储控制器驱动(SATA/NVMe)、文件系统驱动(ext4/xfs/btrfs)、LVM/RAID/加密模块等。

  • /dev/:包含关键设备节点。早期通过静态 mknod 创建(如 /dev/console、/dev/null),现代 initramfs 通常挂载 devtmpfs 动态生成设备文件。

  • /etc/:存放启动配置文件,如 fstab 片段、加密分区的密钥配置、网络启动的 DHCP 参数等。


六、initramfs 的典型应用场景

6.1 核心使用场景

根据 Linux From Scratch 文档,initramfs 的四大主要用途为:

  1. 根文件系统位于网络存储(如 NFS/iSCSI):需先初始化网络栈并挂载远程存储
  2. 根文件系统位于 LVM 逻辑卷:需先激活卷组(vgchange -ay)才能识别逻辑卷
  3. 根文件系统经过加密(如 LUKS):需先调用 cryptsetup luksOpen 解密分区
  4. 使用 UUID/LABEL 标识根分区:initramfs 中的脚本可解析这些标识符并找到对应块设备

6.2 高级应用场景

  • RAID 阵列启动:加载 mdadm 相关模块,组装 RAID 阵列后再挂载
  • Btrfs 多设备启动:处理 Btrfs 的 subvolume 和多设备依赖
  • 休眠恢复(Hibernate):根据 resume= 参数定位 swap 分区并恢复系统状态
  • 安全启动链:在 Secure Boot 环境下,initramfs 可配合 TPM 2.0 进行完整性度量(PCR 8/9 寄存器)
  • Live CD/USB 系统:作为完整的临时操作系统运行,从 squashfs 或 ISO 加载实际系统
  • 嵌入式系统:提供精简的启动环境,支持从 MTD/UBI 设备或 SD 卡启动

七、initramfs 的构建方法

7.1 构建工具链全景

graph TD
    A[构建 initramfs] --> B[手动构建]
    A --> C[自动工具]
    
    B --> B1[cpio -H newc -o]
    B --> B2[gzip/lz4/zstd 压缩]
    B --> B3[嵌入内核 CONFIG_INITRAMFS_SOURCE]
    
    C --> C1[dracut]
    C --> C2[mkinitcpio]
    C --> C3[initramfs-tools/update-initramfs]
    C --> C4[gen_init_cpio 内核工具]
    
    C1 --> C1a[Fedora/RHEL/CentOS/SUSE]
    C2 --> C2a[Arch Linux]
    C3 --> C3a[Debian/Ubuntu]
    C4 --> C4a[内核源码内置]
    
    style C1 fill:#bbf,stroke:#333,stroke-width:2px
    style C2 fill:#bbf,stroke:#333,stroke-width:2px
    style C3 fill:#bbf,stroke:#333,stroke-width:2px

7.2 主流构建工具对比

工具 适用发行版 特点 配置方式
dracut Fedora, RHEL, CentOS, SUSE 模块化设计,自动检测硬件,支持 systemd 集成 /etc/dracut.conf + 模块目录
mkinitcpio Arch Linux, KaOS 基于 hook 的灵活架构,支持自定义钩子 /etc/mkinitcpio.conf + /etc/initcpio/
initramfs-tools Debian, Ubuntu 使用脚本钩子(scripts/init-premount/ 等) /etc/initramfs-tools/ 目录
gen_init_cpio 内核源码内置 编译时嵌入内核,适合嵌入式/自定义构建 CONFIG_INITRAMFS_SOURCE

值得注意的是,KaOS Linux 在 2026.03 版本中已从 mkinitcpio 迁移至 Dracut,以逐步降低对 systemd 早期启动组件的依赖。

7.3 手动构建最小化 initramfs

以下是从零构建 initramfs 的完整流程:

步骤 1:创建目录结构

mkdir -p initramfs/{bin,sbin,lib,dev,proc,sys,etc,mnt/root}
cd initramfs

步骤 2:创建设备节点

sudo mknod -m 600 dev/console c 5 1
sudo mknod -m 666 dev/null c 1 3

步骤 3:集成 BusyBox(静态链接)

# 编译静态 BusyBox
make menuconfig  # 启用 "Build static binary"
make -j$(nproc)
make install CONFIG_PREFIX=/path/to/initramfs

步骤 4:编写 /init 脚本

cat > init <<'EOF'
#!/bin/sh
# 挂载虚拟文件系统
mount -t proc proc /proc
mount -t sysfs sysfs /sys
mount -t devtmpfs devtmpfs /dev

# 加载必要驱动
modprobe ext4

# 挂载真实根文件系统
mount /dev/sda1 /mnt/root

# 切换到真实根并执行 init
exec switch_root /mnt/root /sbin/init
EOF
chmod +x init

步骤 5:打包为 cpio 归档

find . | cpio -H newc -o | gzip -9 > ../initramfs.img

步骤 6:GRUB 配置

menuentry "Custom Linux" {
    linux /boot/vmlinuz root=/dev/sda1 rw
    initrd /boot/initramfs.img
}

7.4 使用 dracut 自动生成

# 生成当前内核的 initramfs
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)

# 强制包含特定驱动
echo 'force_drivers+=" nvme virtio_blk "' > /etc/dracut.conf.d/10-driver.conf

# 添加自定义模块
dracut --add "my-module" -f

7.5 内核内置 initramfs(嵌入式场景)

通过 CONFIG_INITRAMFS_SOURCE 可在编译时将 initramfs 嵌入内核镜像:

# 方法 1:指定目录
CONFIG_INITRAMFS_SOURCE=/path/to/initramfs_dir

# 方法 2:指定描述文件(推荐)
CONFIG_INITRAMFS_SOURCE=/path/to/initramfs_list

描述文件格式示例:

dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
file /bin/busybox /path/to/busybox 755 0 0
file /init /path/to/init.sh 755 0 0

内核构建时,usr/gen_init_cpio 工具会解析该描述文件并生成 initramfs_data.cpio.gz,最终链接到内核的 .init.ramfs 段中。


八、initramfs 的调试与故障排查

8.1 常用调试内核参数

参数 作用
rdinit=/bin/sh 进入 initramfs 的交互式 shell,替代执行 /init
`break=top premount
debug 启用详细调试输出
rootdelay=10 延迟 10 秒挂载根文件系统,等待慢速设备就绪
panic=10 内核 panic 后 10 秒自动重启,避免调试时反复手动重启

8.2 在 initramfs 环境中诊断问题

进入 initramfs shell 后,可执行以下操作:

# 查看已加载模块
lsmod

# 查看块设备
ls /dev/sd* /dev/nvme*
cat /proc/partitions

# 查看内核参数
cat /proc/cmdline

# 测试挂载
mount /dev/sda1 /mnt/root

# 查看内核日志
dmesg | tail -50

# 手动解密 LUKS 分区
cryptsetup luksOpen /dev/sda2 cryptroot

8.3 提取与分析现有 initramfs

# 解压 initramfs(支持多层 cpio 和多种压缩)
mkdir /tmp/initrd && cd /tmp/initrd
zcat /boot/initramfs-$(uname -r).img | cpio -idmv

# 或使用专用工具 3cpio 分析结构
3cpio --examine /boot/initramfs.img
# 输出示例:
# Start     End       Size      Compr.   Extracted
# 0 B       148 kB    148 kB    cpio     147 kB
# 148 kB    13.3 MB   13.1 MB   cpio     13.1 MB
# 55.2 MB   62.0 MB   6.74 MB   zstd     15.6 MB

九、initramfs 的安全考量

9.1 安全启动链中的角色

在现代安全启动(Secure Boot)架构中,initramfs 是信任链的关键一环:

  1. UEFI 固件验证 Bootloader 签名
  2. Bootloader 验证内核镜像与 initramfs 的签名
  3. 内核启用 CONFIG_MODULE_SIG_FORCE,校验所有 .ko 模块签名
  4. TPM 2.0 将内核镜像度量值扩展到 PCR 8,将加载的模块哈希扩展到 PCR 9

9.2 initramfs 的潜在攻击面

  • 未加密传输:网络启动时,initramfs 若通过 HTTP 而非 HTTPS 获取,可能被中间人篡改
  • 敏感数据泄露:initramfs 中可能硬编码磁盘加密密钥或网络认证凭据
  • 模块签名绕过:若未启用强制模块签名,攻击者可通过恶意 .ko 文件在启动早期植入代码

十、总结与展望

initramfs 作为 Linux 启动流程中的“桥梁”组件,成功解决了内核启动时的“先有驱动还是先有文件系统”悖论。从 2.6 内核引入至今,它已从简单的临时根文件系统演变为支持加密、RAID、LVM、网络启动、安全度量等复杂场景的核心基础设施。

其技术演进趋势包括:

  • 统一化:Dracut 逐渐成为跨发行版的事实标准工具
  • 安全强化:与 TPM、Secure Boot、IMA(完整性度量架构)深度集成
  • 轻量化和容器化:在嵌入式和容器场景中,initramfs 被进一步精简为单二进制 + 脚本的最小集合
  • 去 systemd 化:部分发行版(如 KaOS)探索不依赖 systemd 的 initramfs 启动路径

理解 initramfs 的工作原理,不仅是 Linux 系统管理的基础技能,更是进行内核调试、嵌入式开发、安全启动设计的必备知识。


参考资源

原文 https://blog.csdn.net/2301_79518550/article/details/162264778