一步步学习Linux initrd/initramfs

这是一个循序渐进、分为四个阶段的 Linux initrd/initramfs 学习计划。这个计划将引导您从基本概念入手,逐步深入到实践操作、定制化和故障排查,帮助您透彻理解这个关键的启动组件。

学习前的准备 (先决条件)

  1. 一台 Linux 虚拟机: 强烈建议使用虚拟机(如 VirtualBox, VMware, KVM)。这样您可以随意修改配置、破坏和恢复系统,而不用担心损坏您的物理机。推荐使用 CentOS/RHEL/Fedora 或 Debian/Ubuntu。
  2. 基础 Linux 命令 : 熟悉 ls, cd, cp, mv, 文件编辑 (vim, nano),以及基本的 Shell 脚本知识。
  3. 了解 Linux 启动流程: 对从 BIOS/UEFI -> GRUB -> Kernel -> init/systemd 的基本流程有一个宏观的认识。

第一阶段:理论入门 ------ "是什么"与"为什么"

学习目标 : 理解 initrd/initramfs 的存在意义、基本工作原理以及它在 Linux 启动流程中的核心作用。

核心概念:

  • "鸡生蛋,蛋生鸡"问题 : 理解为什么内核需要一个临时的根文件系统。内核启动时,需要驱动程序来访问真正的根文件系统(比如 ext4, xfs),但这些驱动程序(模块 .ko 文件)本身就存放在根文件系统中。initramfs 就是为了解决这个悖论而存在的。
  • initrd vs initramfs :
    • initrd (initial RAM disk): 早期的实现,是一个真实的文件系统镜像(如 ext2),需要内核驱动支持才能挂载。
    • initramfs (initial RAM filesystem): 现代的实现,是一个 cpio 压缩归档。内核可以直接在内存中解压并使用它,无需任何文件系统驱动,效率更高,也更灵活。现在我们通常说的 initrd,实际上指的都是 initramfs
  • 在启动流程中的位置 :
    1. BIOS/UEFI 加载引导加载程序 (Bootloader),如 GRUB。
    2. GRUB 将 Linux 内核 (vmlinuz) 和 initramfs 文件 (initramfs-....img) 加载到内存中。
    3. GRUB 跳转执行内核代码。
    4. 内核初始化核心硬件,然后将内存中的 initramfs 解压,并将其作为临时的根文件系统 (/)。
    5. 内核执行 initramfs 中的 init 脚本(通常是 /init)。
    6. init 脚本加载必要的内核模块(如 NVMe、SATA、LVM、RAID、LUKS 加密等驱动)。
    7. 驱动加载后,init 脚本挂载真正的根文件系统
    8. 最后,通过 switch_root 命令,将根文件系统从临时的 initramfs 切换到真正的物理根文件系统上,并执行真正的 init 进程 (/sbin/init,即 systemd)。

实践操作:

  1. 在您的 Linux 系统中,找到内核和 initramfs 文件。它们通常位于 /boot 目录下。 部分Linux可能仍然使用initrd,如SUSE。

    bash 复制代码
    ls -l /boot/vmlinuz*
    ls -l /boot/initramfs*
  2. 查看 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/
    • 一些轻量级的核心工具(通常由 busyboxklish 提供),如 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 底层的归档工具。

实践操作:

  1. 查看 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) .
  2. 进入解压后的目录 ~/initramfs_contents,用 ls -R 查看整个目录结构。

  3. 重点阅读 init 脚本 。虽然可能很长,但尝试理解它的基本逻辑:解析内核参数 -> 加载驱动 -> 寻找并挂载根分区 -> switch_root

  4. 重新生成 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)相关的内核模块?
  • dracutupdate-initramfs 是如何决定哪些模块和工具应该被包含进去的?(提示:查看它们的配置文件,如 /etc/dracut.conf.d/

第三阶段:动手定制 ------ "修改"与"扩展"

学习目标 : 学会如何向 initramfs 中添加自定义的模块、驱动或脚本,以满足特殊需求。

核心概念:

  • 定制化的场景 :
    • 添加一个内核默认没包含的第三方驱动(如特殊的网卡或存储卡驱动)。
    • 嵌入一个二进制文件或脚本,在系统启动早期执行特定任务(如解密、网络配置)。
    • 添加调试工具。
  • 通过 dracut 定制 :
    • 配置文件: /etc/dracut.conf 或在 /etc/dracut.conf.d/ 目录下创建 .conf 文件。
    • add_drivers / force_drivers: 强制添加内核模块。
    • install_items: 添加额外的文件或可执行程序。

实践操作 (以 dracut 为例):

  1. 目标 : 创建一个简单的脚本,在 initramfs 启动阶段打印一条 "Hello from custom initramfs!" 的消息,然后将其添加到 initramfs 中。

  2. 创建自定义模块目录 :

    bash 复制代码
    sudo mkdir -p /usr/lib/dracut/modules.d/99my-hello
  3. 创建模块控制文件 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"
    }
  4. 创建钩子脚本 hello.sh :

    bash 复制代码
    # /usr/lib/dracut/modules.d/99my-hello/hello.sh
    #!/bin/bash
    
    echo "========================================="
    echo " Hello from custom initramfs!            "
    echo "========================================="
    sleep 5 # 暂停5秒,方便查看
  5. 设置权限并重新生成 initramfs :

    bash 复制代码
    sudo 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
  6. 重启虚拟机,在启动的早期阶段,您应该能在屏幕上看到打印出的 "Hello" 消息。

思考与验证:

  • 如果我想让一个闭源的网卡驱动 proprietary_net.koinitramfs 中就被加载,我应该如何修改 dracut 的配置?
  • 定制 initramfs 会带来哪些潜在的风险?(如体积过大、启动变慢、引入不稳定因素)

第四阶段:故障排查 ------ "调试"与"修复"

学习目标 : 掌握 initramfs 阶段常见问题的排查方法,能够在系统无法启动时进入紧急 Shell 并进行修复。

核心概念:

  • 常见错误 : 最经典的错误是 "Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)"。这几乎总是意味着 initramfs 中缺少必要的驱动来识别或挂载根文件系统。
  • rd.break : 一个非常有用的内核启动参数。它会在 initramfsinit 脚本执行完毕、即将 switch_root 之前中断,并提供一个交互式的 Shell。
  • 紧急 Shell (Emergency Shell) : 在这个 Shell 中,您可以:
    • 使用 lsmod 检查已加载的模块。
    • 使用 modprobe 手动加载模块。
    • 查看日志 (dmesg)。
    • 检查设备 (ls /dev)。
    • 手动执行 LVM、RAID 或 cryptsetup 命令来激活存储。

实践操作:

  1. 模拟一次故障 :
    • 在您的虚拟机中,通过 dracut 的配置文件,故意排除 掉您根分区所需的驱动。例如,如果您的根分区是 ext4 on virtio_blk,可以尝试在 /etc/dracut.conf.d/ 中创建一个文件,内容为 omit_drivers+=" virtio_blk "
    • 执行 sudo dracut --force 重建 initramfs
    • 重启虚拟机。此时系统应该会启动失败,并进入 dracut 的紧急 Shell 或 panic。
  2. 使用 rd.break 进行调试 :
    • 再次重启虚拟机,在 GRUB 菜单界面,按 e 编辑启动项。
    • 在以 linuxlinuxefi 开头的那一行的末尾,添加 rd.break
    • Ctrl+xF10 启动。
    • 系统会启动并最终停在一个 switch_root:/# 的提示符下。
  3. 在紧急 Shell 中排查 :
    • lsmod | grep virtio_blk (会发现没有输出)。
    • modprobe virtio_blk (手动加载缺失的模块)。
    • ls /dev/vda* (现在应该能看到您的磁盘设备了)。
    • 此时,您可以手动 mount /dev/vdaX /sysroot,然后 exit 退出紧急 Shell,系统将继续正常启动。
  4. 修复问题 :
    • 系统正常启动后,移除您之前添加的 omit_drivers 配置。
    • 执行 sudo dracut --force 重新生成一个正确的 initramfs

思考与验证:

  • 除了 rd.break,还有哪些内核参数可以影响 initramfs 的行为?(提示: rd.shell, rd.debug
  • 如果在紧急 Shell 中,发现连键盘都无法使用,可能是什么原因导致的?(initramfs 中缺少 USB/HID 驱动)

完成这四个阶段的学习后,您将对 Linux 的 initramfs 有一个非常扎实和深入的理解,并具备在实际工作中分析、定制和修复相关问题的能力。

相关推荐
AOwhisky21 分钟前
Linux 文本处理三剑客:awk、grep、sed 完全指南
linux·运维·服务器·网络·云计算·运维开发
Gavin_91544 分钟前
从零开始部署经典开源项目管理系统最新版redmine6-Linux Debian12
linux·ruby on rails·开源·debian·ruby·redmine
花小璇学linux1 小时前
imx6ull-驱动开发篇31——Linux异步通知
linux·驱动开发·嵌入式软件
shelutai1 小时前
ubuntu 编译ffmpeg6.1 增加drawtext,libx264,libx265等
linux·ubuntu·ffmpeg
runfarther2 小时前
搭建LLaMA-Factory环境
linux·运维·服务器·python·自然语言处理·ai编程·llama-factory
hello_ world.2 小时前
RHCA10NUMA
linux
神秘人X7072 小时前
Linux高效备份:rsync + inotify实时同步
linux·服务器·rsync
轻松Ai享生活3 小时前
一步步深入学习Linux Process Scheduling
linux
绵绵细雨中的乡音4 小时前
网络基础知识
linux·网络