内核不可能编译进所有存储驱动——NVMe、RAID 控制器、LUKS 加密、LVM 的驱动从哪来?答案是 initramfs:一个临时的根文件系统,提供启动必需的模块和工具。
一、先看图
flowchart LR
BL[Bootloader] -->|加载| KERNEL[vmlinuz]
BL -->|加载| INITRD[initramfs.img<br/>cpio 归档]
KERNEL --> UNPACK[解包 initramfs<br/>到 rootfs]
UNPACK --> INIT_E[/init 脚本<br/>加载驱动/解密]
INIT_E --> MOUNT[mount 真正 rootfs]
MOUNT --> SWITCH[switch_root<br/>切换到真 root]
SWITCH --> PID1[PID 1<br/>systemd]
classDef early fill:#f0883e22,stroke:#f0883e,color:#adbac7;
classDef final fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class BL,KERNEL,INITRD early
class UNPACK,INIT_E,MOUNT,SWITCH early
class PID1 final
二、为什么需要 initramfs
内核 vmlinuz 不能包含所有驱动 → root 文件系统可能在:
- NVMe(需要 nvme 驱动)
- RAID(需要 md 驱动)
- LUKS 加密(需要 dm-crypt + 密码输入)
- LVM(需要 device-mapper)
- 网络(iSCSI / NFS root)
initramfs 提供这些模块 + 必要的用户态工具。
三、格式
initramfs 是 cpio 归档(可能 gzip/xz/zstd 压缩):
# 查看内容
lsinitramfs /boot/initramfs-$(uname -r).img
# 手动解包
mkdir /tmp/initrd && cd /tmp/initrd
zcat /boot/initramfs-$(uname -r).img | cpio -idmv四、/init 脚本
initramfs 中的 /init(或 systemd):
- 加载必要的内核模块
- 检测并激活 RAID/LVM
- 解密 LUKS
- mount 真正的 root 文件系统
switch_root切换
五、dracut
Fedora/RHEL/SUSE 使用 dracut 生成 initramfs:
dracut --force /boot/initramfs-$(uname -r).img $(uname -r)5.1 模块化
dracut --list-modules
# base, bash, dm, lvm, luks, network, nfs, ...每个 dracut 模块提供特定功能的脚本和工具。
5.2 配置
# /etc/dracut.conf.d/custom.conf
add_dracutmodules+=" lvm dm "
compress="zstd"六、mkinitramfs / mkinitcpio
Debian 用 mkinitramfs,Arch 用
mkinitcpio——功能类似,生态不同。
七、Unified Kernel Image(UKI)
UKI = UEFI stub + 内核 + initramfs + 命令行 + 签名
一个文件 → UEFI 直接启动 → Secure Boot 签名覆盖全部。
# systemd-ukify 构建
ukify build \
--linux /boot/vmlinuz \
--initrd /boot/initrd.img \
--cmdline "root=/dev/sda2" \
--output /boot/efi/EFI/Linux/linux.efi八、switch_root
// util-linux: switch_root
mount(new_root, "/", NULL, MS_MOVE, NULL);
chroot(".");
chdir("/");
// exec real init
execv("/sbin/init", argv);关键:switch_root 删除 initramfs 中的所有文件 → 释放内存 → exec PID 1。
九、观察
# 当前 initramfs
ls -lh /boot/initramfs-$(uname -r).img
# 内容
lsinitramfs /boot/initramfs-$(uname -r).img | head -20
# 启动日志中的 initramfs 阶段
dmesg | grep -i "initramfs\|switch_root\|Trying to unpack"
# dracut 重新生成
dracut --force --verbose十、小结
- initramfs 是临时根文件系统 → 提供启动必需的驱动和工具
- cpio 归档格式 + 压缩
- dracut/mkinitramfs 生成
- switch_root 切换到真正 rootfs
- UKI 把内核+initramfs 打包为单个签名 EFI 应用
参考文献
Documentation/filesystems/ramfs-rootfs-initramfs.rstusr/gen_init_cpio.c(内核内置 initramfs 工具)- dracut documentation
- systemd-ukify(1)
工具
dracut/mkinitramfs/mkinitcpiolsinitramfs/lsinitrdukify
文章导航
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
CPU 调度:CFS 的虚拟运行时间与 EEVDF 的虚拟截止期
从比例份额调度的论文谱系出发,对照 Linux v6.6 与 v6.12 fair.c 源码,解释 CFS 的 vruntime、EEVDF 的 lag/eligible/deadline,并用确定性模拟复现延迟与 lag 的差异。
I/O 调度:在寻道、队列深度与公平性之间取舍
从电梯算法到 blk-mq,解释 Linux I/O 调度器为何在 HDD 上排序、在共享设备上保公平、在 NVMe 上常选择 none,并用确定性模拟展示寻道距离与队列尾延迟的取舍。
用户态内存分配器:size class、线程缓存与碎片边界
从 Wilson 分配器综述到 jemalloc、gperftools tcmalloc 与 mimalloc 的源码路径,解释 size class、线程缓存、跨线程释放和 RSS 碎片;用可复现实验说明不能只凭 allocator 名字下结论。
伙伴系统与 SLUB:Linux 物理页和小对象分配的边界
以 Linux v6.6 源码和确定性模拟为准,解释伙伴系统的 split/coalesce、migratetype 反碎片策略,以及 SLUB 的 per-CPU freelist、partial list 与安全加固。