一、引言:为什么需要 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 月的内核邮件列表讨论。核心原因包括:
- 格式简洁性:cpio 的 “newc” 格式比 tar 的数十种变体更简单,内核解压代码仅约 26KB
- 标准兼容性:cpio 是 AT&T 时代的 Unix 标准,已被 RPM 等工具广泛使用
- 内核自包含:内核内置 cpio 解压器,无需依赖外部工具即可在启动早期解压
- 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 的四大主要用途为:
- 根文件系统位于网络存储(如 NFS/iSCSI):需先初始化网络栈并挂载远程存储
- 根文件系统位于 LVM 逻辑卷:需先激活卷组(
vgchange -ay)才能识别逻辑卷 - 根文件系统经过加密(如 LUKS):需先调用
cryptsetup luksOpen解密分区 - 使用 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 是信任链的关键一环:
- UEFI 固件验证 Bootloader 签名
- Bootloader 验证内核镜像与 initramfs 的签名
- 内核启用
CONFIG_MODULE_SIG_FORCE,校验所有.ko模块签名 - 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 系统管理的基础技能,更是进行内核调试、嵌入式开发、安全启动设计的必备知识。
参考资源
- Linux 内核文档: ramfs-rootfs-initramfs.txt — 官方权威说明
- Gentoo Wiki: Initramfs — 构建指南与概念解释
- Linux From Scratch: About initramfs — 用途与构建原理
- CPIO 格式规范 — 技术实现细节
- SUSE 管理指南: 引导过程与 initramfs — 企业级发行版实践
原文 https://blog.csdn.net/2301_79518550/article/details/162264778