initrd遇到的问题及initramfs相关

问题背景

最新在升级新驱动时遇到了一个很奇葩的问题:在客户某个版本的系统下,不管是将核外驱动包以前的老版本卸载后再安装新版本,还是直接升级该核外驱动包的新版本,从加载的驱动中读出的驱动版本一直是老的,甚至将该驱动包卸载后驱动还会加载,且为老版本,查找了一圈发现连KO都已经不存在了,奇了怪了,最后就把目光怀疑到了 **initrd**,果然在initrd里面有这个wifi驱动。

那它是怎么进去的呢?默认的initrd是没有将该wifi驱动放在里面的,后来发现居然是其他驱动的dkms包hooks脚本暴力将整个updates/dkms目录拉进了initrd ~~

下面,我们来看看这个 initrdinitramfs, 以下主要以 Debian/Ubuntu 系的发行版为例。

initrd与initramfs

initramfsinitrd 都是 Linux 启动过程中用于临时根文件系统(initial root filesystem)的机制,目的是在挂载真正的根文件系统之前加载必要的驱动、模块和工具。虽然它们目标一致,但在实现方式、历史演进和现代使用上存在关键区别。

一、基本概念对比

特性 initrd(Initial RAM Disk) initramfs(Initial RAM File System)
出现时间 较早(Linux 2.4 时代) 较新(Linux 2.6 起成为主流)
技术基础 块设备镜像(模拟磁盘),需格式化为 ext2 等文件系统 cpio 归档 + tmpfs,直接作为内存中的文件系统
加载方式 内核将其当作一个 RAM disk 挂载 内核直接解压 cpio 到 tmpfs 中
内存效率 较低(固定大小,即使未用满也占用内存) 较高(按需分配,动态增长/收缩)
复杂度 需要文件系统驱动支持(如 ext2) 无需额外文件系统,内核原生支持 cpio
现代使用 ❌ 已基本被 initramfs 取代 ✅ 当前所有主流发行版默认使用

二、技术细节差异

1. initrd 的工作流程

  1. Bootloader(如 GRUB)将 vmlinuz(内核)和 initrd.img 加载到内存。
  2. 内核创建一个 RAM disk 设备(如 /dev/ram0)。
  3. initrd.img 解压并挂载为一个块设备上的文件系统(通常是 ext2)。
  4. 执行其中的 /linuxrc 脚本。
  5. 脚本完成后,卸载 initrd,挂载真正的根文件系统。

⚠️ 缺点: 需要内核内置对应文件系统的驱动(如 ext2),且内存浪费大。

2. initramfs 的工作流程

  1. Bootloader 加载内核和 initramfs(通常也是叫 initrd.img,但内容是 cpio 格式)。
  2. 内核将 cpio 归档直接解压到 tmpfs(基于内存的临时文件系统)中。
  3. 执行 /init 脚本(不再是 /linuxrc)。
  4. 脚本完成必要操作(如加载加密/LVM/RAID 驱动)后,通过 switch_root 切换到真正的根文件系统。

优点:

  • 不依赖特定文件系统;
  • 内存使用更高效;
  • 实现更简洁,集成度更高。

三、文件命名的“混淆”

尽管现代系统几乎都使用 initramfs 技术,但出于兼容性和习惯,生成的文件仍常命名为:

  • /boot/initrd.img-<kernel-version>(Debian/Ubuntu)

🔍 如何判断实际类型?

file /boot/initrd.img-$(uname -r)

initramfs会输出类似结果:
initrd.img-5.4.18-142-generic: ASCII cpio archive (SVR4 with no CRC)


在今天谈论 “initrd” 时,绝大多数情况下实际指的是 initramfs,通常仍叫 initrd.img,但内容是 initramfs。
真正的 initrd 已成为历史,仅在非常老的系统或特殊嵌入式场景中存在。

initramfs 管理

Debian/Ubuntu 系的发行版中主要使用 initramfs-tools 来生成和管理 initramfs 。
它在系统安装、内核更新或手动配置时自动构建 /boot/initrd.img-* 文件,确保系统启动时能加载必要的驱动。

一、核心组件

组件 说明
/usr/sbin/update-initramfs 主命令行工具,用于创建/更新/删除 initramfs
/etc/initramfs-tools/ 配置目录,控制 initramfs 内容
/usr/share/initramfs-tools/ 脚本和模块模板(hooks、scripts 等)

二、常用命令:update-initramfs

基本语法

sudo update-initramfs -[c|u|d] -k <kernel-version>
选项 含义
-c create:为指定内核创建新的 initramfs
-u update:更新现有 initramfs(最常用)
-d delete:删除指定内核的 initramfs
-k all 对所有已安装内核操作
-k $(uname -r) 仅对当前运行内核操作(默认行为)

常用示例

1. 更新当前内核的 initramfs(最常见)
sudo update-initramfs -u
# 等价于
sudo update-initramfs -u -k $(uname -r)
2. 为所有内核更新 initramfs
sudo update-initramfs -u -k all
3. 手动为内核创建 initramfs
sudo update-initramfs -c -k 6.8.0-40-generic

三、关键配置目录:/etc/initramfs-tools/

1. /etc/initramfs-tools/modules

显式指定要包含的内核模块(每行一个):

# 示例:强制包含 USB 存储和 NVMe 驱动
usb_storage
nvme

⚠️ 仅当自动依赖分析未包含必要模块时才需手动添加。

2. /etc/initramfs-tools/conf.d/

存放配置片段文件(如 modules, compress, cryptroot 等)。

3. /etc/initramfs-tools/scripts/

自定义启动脚本目录,分阶段执行:

子目录 执行时机
init-top/ 最早执行(挂载根文件系统前)
init-premount/ 挂载根文件系统前
local-top/ 根设备已发现但未挂载
local-bottom/ 根文件系统已挂载,即将 switch_root

4. /etc/modprobe.d/

虽然不属于 initramfs-tools 直接管理,但其中的 blacklist 规则会影响模块是否被包含:

# /etc/modprobe.d/blacklist-mydriver.conf
blacklist problematic_driver

配合 update-initramfs -u 可从 initramfs 中移除该驱动。

initramfs 调试与验证

1. 查看 initramfs 内容

# 列出文件(无需解压)
lsinitramfs /boot/initrd.img-$(uname -r)

# 或传统方式
zcat /boot/initrd.img-$(uname -r) | cpio -it

2. 解包 initramfs(用于调试)

mkdir /tmp/initrd && cd /tmp/initrd
zcat /boot/initrd.img-$(uname -r) | cpio -idmv

initramfs 典型使用场景

场景 1:添加缺失的存储驱动

系统无法识别 NVMe 硬盘?
→ 编辑 /etc/initramfs-tools/modules 添加 nvme
→ 运行 sudo update-initramfs -u

场景 2:排除有问题的驱动

某个驱动导致启动卡死?
→ 在 /etc/modprobe.d/blacklist.conf 中加入 blacklist bad_driver
→ 运行 sudo update-initramfs -u

场景 3:自定义启动逻辑

需要在挂载根分区前执行脚本?
→ 将脚本放入 /etc/initramfs-tools/scripts/local-top/
→ 设为可执行:chmod +x script.sh
→ 运行 sudo update-initramfs -u

initramfs 注意事项

  • 不要随意删除 initramfs,否则系统可能无法启动。
  • 修改配置后必须运行 update-initramfs -u 才能生效。
  • 自动更新:通过 apt upgrade 安装新内核时,系统会自动调用 update-initramfs -c 为新内核生成镜像。
  • initramfs 大小:包含过多模块会增大镜像,延长启动时间,建议仅保留必要驱动。