这是一个循序渐进、分为四个阶段的 Linux initrd/initramfs 学习计划。这个计划将引导您从基本概念入手,逐步深入到实践操作、定制化和故障排查,帮助您透彻理解这个关键的启动组件。
学习前的准备 (先决条件)
- 一台 Linux 虚拟机: 强烈建议使用虚拟机(如 VirtualBox, VMware, KVM)。这样您可以随意修改配置、破坏和恢复系统,而不用担心损坏您的物理机。推荐使用 CentOS/RHEL/Fedora 或 Debian/Ubuntu。
- 基础 Linux 命令 : 熟悉
ls
,cd
,cp
,mv
, 文件编辑 (vim
,nano
),以及基本的 Shell 脚本知识。 - 了解 Linux 启动流程: 对从 BIOS/UEFI -> GRUB -> Kernel -> init/systemd 的基本流程有一个宏观的认识。
第一阶段:理论入门 ------ "是什么"与"为什么"
学习目标 : 理解 initrd
/initramfs
的存在意义、基本工作原理以及它在 Linux 启动流程中的核心作用。
核心概念:
- "鸡生蛋,蛋生鸡"问题 : 理解为什么内核需要一个临时的根文件系统。内核启动时,需要驱动程序来访问真正的根文件系统(比如
ext4
,xfs
),但这些驱动程序(模块.ko
文件)本身就存放在根文件系统中。initramfs
就是为了解决这个悖论而存在的。 initrd
vsinitramfs
:initrd
(initial RAM disk): 早期的实现,是一个真实的文件系统镜像(如 ext2),需要内核驱动支持才能挂载。initramfs
(initial RAM filesystem): 现代的实现,是一个cpio
压缩归档。内核可以直接在内存中解压并使用它,无需任何文件系统驱动,效率更高,也更灵活。现在我们通常说的initrd
,实际上指的都是initramfs
。
- 在启动流程中的位置 :
- BIOS/UEFI 加载引导加载程序 (Bootloader),如 GRUB。
- GRUB 将 Linux 内核 (
vmlinuz
) 和initramfs
文件 (initramfs-....img
) 加载到内存中。 - GRUB 跳转执行内核代码。
- 内核初始化核心硬件,然后将内存中的
initramfs
解压,并将其作为临时的根文件系统 (/
)。 - 内核执行
initramfs
中的init
脚本(通常是/init
)。 init
脚本加载必要的内核模块(如 NVMe、SATA、LVM、RAID、LUKS 加密等驱动)。- 驱动加载后,
init
脚本挂载真正的根文件系统。 - 最后,通过
switch_root
命令,将根文件系统从临时的initramfs
切换到真正的物理根文件系统上,并执行真正的init
进程 (/sbin/init
,即systemd
)。
实践操作:
-
在您的 Linux 系统中,找到内核和
initramfs
文件。它们通常位于/boot
目录下。 部分Linux可能仍然使用initrd,如SUSE。bashls -l /boot/vmlinuz* ls -l /boot/initramfs*
-
查看 GRUB 的配置文件(如
/boot/grub2/grub.cfg
或/boot/grub/grub.cfg
),找到menuentry
部分,看看它是如何指定linux
(内核) 和initrd
(initramfs 文件) 这两行配置的。
思考与验证:
- 尝试用自己的话解释,如果服务器的根文件系统在 LVM 或者一个特殊的 RAID 卡上,为什么
initramfs
是必需的? initramfs
和常规的根文件系统有什么本质区别?
第二阶段:内部探秘 ------ "有什么"与"怎么生成"
学习目标 : 学会如何查看 initramfs
的内容,并了解用于创建和管理它的核心工具。
核心概念:
initramfs
的结构 : 它是一个微型的 Linux 根文件系统,包含:- 一个
init
脚本,是整个initramfs
阶段的执行入口。 - 必要的内核模块 (
.ko
文件),存放于/lib/modules/
。 - 一些轻量级的核心工具(通常由
busybox
或klish
提供),如mount
,insmod
,lvm
,cryptsetup
。 - 设备文件 (
/dev
) 和配置文件 (/etc
)。
- 一个
- 核心工具 :
dracut
(用于 RHEL/CentOS/Fedora 等): 一个高度模块化和可扩展的initramfs
生成工具。mkinitcpio
(用于 Arch Linux)。update-initramfs
(用于 Debian/Ubuntu):mkinitramfs
的前端。lsinitrd
(dracut 提供) 或unmkinitramfs
: 用于列出和解压initramfs
文件的内容。cpio
:initramfs
底层的归档工具。
实践操作:
-
查看
initramfs
内容 :bash# 在基于 dracut 的系统上 lsinitrd /boot/initramfs-$(uname -r).img # 你也可以将它解压出来细看 mkdir ~/initramfs_contents cd ~/initramfs_contents # 如果有 lsinitrd lsinitrd /boot/initramfs-$(uname -r).img | cpio -idmv # 或者对于 Debian/Ubuntu # unmkinitramfs /boot/initrd.img-$(uname -r) .
-
进入解压后的目录
~/initramfs_contents
,用ls -R
查看整个目录结构。 -
重点阅读
init
脚本 。虽然可能很长,但尝试理解它的基本逻辑:解析内核参数 -> 加载驱动 -> 寻找并挂载根分区 ->switch_root
。 -
重新生成
initramfs
:bash# 在基于 dracut 的系统上 (先备份!) sudo cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak sudo dracut --force --verbose # 在 Debian/Ubuntu 上 (先备份!) sudo cp /boot/initrd.img-$(uname -r) /boot/initrd.img-$(uname -r).bak sudo update-initramfs -u
思考与验证:
- 在您解压的
initramfs
中,找到了哪些与存储(sata, nvme, scsi)或文件系统(ext4, xfs, lvm)相关的内核模块? dracut
或update-initramfs
是如何决定哪些模块和工具应该被包含进去的?(提示:查看它们的配置文件,如/etc/dracut.conf.d/
)
第三阶段:动手定制 ------ "修改"与"扩展"
学习目标 : 学会如何向 initramfs
中添加自定义的模块、驱动或脚本,以满足特殊需求。
核心概念:
- 定制化的场景 :
- 添加一个内核默认没包含的第三方驱动(如特殊的网卡或存储卡驱动)。
- 嵌入一个二进制文件或脚本,在系统启动早期执行特定任务(如解密、网络配置)。
- 添加调试工具。
- 通过
dracut
定制 :- 配置文件:
/etc/dracut.conf
或在/etc/dracut.conf.d/
目录下创建.conf
文件。 add_drivers
/force_drivers
: 强制添加内核模块。install_items
: 添加额外的文件或可执行程序。
- 配置文件:
实践操作 (以 dracut
为例):
-
目标 : 创建一个简单的脚本,在
initramfs
启动阶段打印一条 "Hello from custom initramfs!" 的消息,然后将其添加到initramfs
中。 -
创建自定义模块目录 :
bashsudo mkdir -p /usr/lib/dracut/modules.d/99my-hello
-
创建模块控制文件
module-setup.sh
:bash# /usr/lib/dracut/modules.d/99my-hello/module-setup.sh #!/bin/bash # 检查是否需要安装此模块 check() { return 0 } # 定义依赖 depends() { return 0 } # 安装脚本和钩子 install() { # 安装一个钩子脚本到 pre-mount 阶段 inst_hook pre-mount 99 "${moddir}/hello.sh" }
-
创建钩子脚本
hello.sh
:bash# /usr/lib/dracut/modules.d/99my-hello/hello.sh #!/bin/bash echo "=========================================" echo " Hello from custom initramfs! " echo "=========================================" sleep 5 # 暂停5秒,方便查看
-
设置权限并重新生成
initramfs
:bashsudo chmod +x /usr/lib/dracut/modules.d/99my-hello/module-setup.sh sudo chmod +x /usr/lib/dracut/modules.d/99my-hello/hello.sh sudo dracut --force
-
重启虚拟机,在启动的早期阶段,您应该能在屏幕上看到打印出的 "Hello" 消息。
思考与验证:
- 如果我想让一个闭源的网卡驱动
proprietary_net.ko
在initramfs
中就被加载,我应该如何修改dracut
的配置? - 定制
initramfs
会带来哪些潜在的风险?(如体积过大、启动变慢、引入不稳定因素)
第四阶段:故障排查 ------ "调试"与"修复"
学习目标 : 掌握 initramfs
阶段常见问题的排查方法,能够在系统无法启动时进入紧急 Shell 并进行修复。
核心概念:
- 常见错误 : 最经典的错误是 "Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)"。这几乎总是意味着
initramfs
中缺少必要的驱动来识别或挂载根文件系统。 rd.break
: 一个非常有用的内核启动参数。它会在initramfs
的init
脚本执行完毕、即将switch_root
之前中断,并提供一个交互式的 Shell。- 紧急 Shell (Emergency Shell) : 在这个 Shell 中,您可以:
- 使用
lsmod
检查已加载的模块。 - 使用
modprobe
手动加载模块。 - 查看日志 (
dmesg
)。 - 检查设备 (
ls /dev
)。 - 手动执行 LVM、RAID 或 cryptsetup 命令来激活存储。
- 使用
实践操作:
- 模拟一次故障 :
- 在您的虚拟机中,通过
dracut
的配置文件,故意排除 掉您根分区所需的驱动。例如,如果您的根分区是ext4
onvirtio_blk
,可以尝试在/etc/dracut.conf.d/
中创建一个文件,内容为omit_drivers+=" virtio_blk "
。 - 执行
sudo dracut --force
重建initramfs
。 - 重启虚拟机。此时系统应该会启动失败,并进入
dracut
的紧急 Shell 或 panic。
- 在您的虚拟机中,通过
- 使用
rd.break
进行调试 :- 再次重启虚拟机,在 GRUB 菜单界面,按
e
编辑启动项。 - 在以
linux
或linuxefi
开头的那一行的末尾,添加rd.break
。 - 按
Ctrl+x
或F10
启动。 - 系统会启动并最终停在一个
switch_root:/#
的提示符下。
- 再次重启虚拟机,在 GRUB 菜单界面,按
- 在紧急 Shell 中排查 :
lsmod | grep virtio_blk
(会发现没有输出)。modprobe virtio_blk
(手动加载缺失的模块)。ls /dev/vda*
(现在应该能看到您的磁盘设备了)。- 此时,您可以手动
mount /dev/vdaX /sysroot
,然后exit
退出紧急 Shell,系统将继续正常启动。
- 修复问题 :
- 系统正常启动后,移除您之前添加的
omit_drivers
配置。 - 执行
sudo dracut --force
重新生成一个正确的initramfs
。
- 系统正常启动后,移除您之前添加的
思考与验证:
- 除了
rd.break
,还有哪些内核参数可以影响initramfs
的行为?(提示:rd.shell
,rd.debug
) - 如果在紧急 Shell 中,发现连键盘都无法使用,可能是什么原因导致的?(
initramfs
中缺少 USB/HID 驱动)
完成这四个阶段的学习后,您将对 Linux 的 initramfs
有一个非常扎实和深入的理解,并具备在实际工作中分析、定制和修复相关问题的能力。