grub中vmlinuz initramfs分析
grub中vmlinuz initramfs分析
linux grub中vmlinuz initramfs在系统启动中的作用?
在 Linux 系统启动过程中,vmlinuz 和 initramfs 是 GRUB 加载的两个关键组件,它们分别承担内核初始化和临时根文件系统准备的任务。以下是它们的详细作用及协作流程:
1. vmlinuz:压缩的 Linux 内核
作用
• 核心功能:包含 Linux 内核的压缩镜像(通常为 zImage 或 bzImage),负责初始化硬件、加载驱动、挂载根文件系统,并最终启动用户空间的 init 进程。
• 关键任务:
• 检测和初始化 CPU、内存、存储控制器等硬件。
• 加载必要的内核模块(如文件系统驱动、磁盘控制器驱动)。
• 移交控制权给用户空间(通过 initramfs 或直接挂载根文件系统)。
GRUB 中的配置
• 在 GRUB 配置文件(如 /boot/grub/grub.cfg)中通过 linux 指令加载:
1 | |
• root= 指定根文件系统设备。
• ro 表示以只读模式挂载根文件系统(后续可能切换为读写)。
2. initramfs:初始 RAM 文件系统
作用
• 临时根文件系统:是一个包含工具和内核模块的 CPIO 归档,在内存中解压成临时根目录(/),用于在真实根文件系统挂载前提供关键环境。
• 解决依赖问题:
• 加载存储设备驱动(如 NVMe、RAID、LVM 驱动)。
• 解密加密的根文件系统(如 LUKS)。
• 挂载复杂存储设备(如网络存储 NFS、iSCSI)。
• 灵活性与模块化:允许内核动态加载所需模块,避免将全部驱动编译进内核。
GRUB 中的配置
• 通过 initrd 指令加载,与 vmlinuz 配合使用:
1 | |
3. 启动流程详解
sequenceDiagram
participant GRUB
participant vmlinuz
participant initramfs
participant RootFS
GRUB->>vmlinuz: 加载内核镜像到内存
GRUB->>initramfs: 加载initramfs到内存
vmlinuz->>initramfs: 解压并挂载为临时根文件系统
initramfs->>initramfs: 执行/init脚本(或/linuxrc)
initramfs->>initramfs: 加载驱动(如dm-crypt、ext4)
initramfs->>RootFS: 挂载真实的根文件系统
initramfs->>vmlinuz: 切换根目录(pivot_root)
vmlinuz->>RootFS: 启动/sbin/init(systemd/sysvinit)
关键步骤
- GRUB 阶段:
• 读取配置文件,加载vmlinuz和initramfs到内存。 - 内核初始化:
•vmlinuz解压并启动,但此时无法直接访问真实的根文件系统(因驱动未加载或存储未解密)。 - initramfs 接管:
• 提供临时环境,加载必要的驱动和工具(如cryptsetup、lvm2)。
• 挂载真实的根文件系统到/sysroot。 - 根切换:
• 执行pivot_root将/sysroot切换为新的根目录/。 - 用户空间启动:
• 内核启动/sbin/init(如 systemd),进入常规系统初始化流程。
4. 为什么需要 initramfs?
• 硬件多样性:现代存储设备(如 NVMe、RAID)需要内核模块支持,但这些模块可能存储在未挂载的根文件系统中,形成“鸡生蛋”问题。
• 加密与网络根:解密 LUKS 分区或挂载 NFS 根文件系统需要提前加载工具和配置。
• 灵活性:允许发行版为不同硬件生成定制的 initramfs,而无需重新编译内核。
5. 无 initramfs 的极简场景
若内核已内置所有必要驱动且根文件系统无加密/复杂配置,可直接启动:
1 | |
适用条件:
• 根文件系统在简单设备上(如 ext4 格式的 SATA 磁盘)。
• 内核已编译所有必需驱动(无模块依赖)。
6. 调试与自定义
(1) 查看 initramfs 内容
1 | |
(2) 重建 initramfs
1 | |
(3) 启动时进入 initramfs Shell
在 GRUB 启动菜单中编辑内核参数,添加 break 或 rd.break:
1 | |
启动后会暂停在 initramfs 的调试 Shell,便于排查挂载问题。
7. 总结
| 组件 | 作用 | 是否必需 |
|---|---|---|
vmlinuz |
内核本体,初始化硬件并启动系统。 | 是 |
initramfs |
提供临时根文件系统,加载驱动/工具以挂载真实根文件系统。 | 依赖硬件和根文件系统复杂度 |
| 协作关系 | vmlinuz 依赖 initramfs 解决早期启动依赖,最终移交控制权给用户空间。 |
通过 vmlinuz 和 initramfs 的分工协作,Linux 系统能够灵活应对各种硬件和存储配置,确保启动过程的可靠性和可扩展性。
也就是一般将驱动放到initramfs中?
是的!通常会将关键驱动(尤其是存储和文件系统相关驱动)打包到 initramfs 中,以确保内核在挂载真实根文件系统前能正确识别硬件。以下是具体原因和实现方式:
1. 为什么驱动要放到 initramfs 中?
(1) 解决“鸡生蛋”问题
• 场景:根文件系统存储在需要驱动的设备上(如 NVMe、RAID、LUKS 加密分区),但这些驱动可能存放在未挂载的根文件系统中。
• 方案:initramfs 提前加载驱动,打破依赖循环。
(2) 模块化内核的灵活性
• 现代 Linux 内核默认以模块化方式编译驱动(如 ext4.ko、nvme.ko),而非全部内置。
• initramfs 包含这些模块,避免内核体积膨胀。
(3) 支持复杂存储配置
• 加密(LUKS)、逻辑卷(LVM)、网络存储(NFS/iSCSI)等需要额外工具(如 cryptsetup、lvm2),这些工具依赖的驱动和库也需放入 initramfs。
2. 哪些驱动会被放入 initramfs?
• 存储控制器驱动:NVMe、AHCI、SCSI、RAID(mdadm)、USB 存储。
• 文件系统驱动:ext4、xfs、btrfs、f2fs。
• 加密和卷管理:dm-crypt(LUKS)、dm-mod(LVM)。
• 关键硬件驱动:部分 GPU 驱动(如 fbdev)、键盘输入(用于解密密码)。
3. 驱动如何被添加到 initramfs?
(1) 自动生成(推荐)
工具(如 mkinitramfs、dracut)会扫描系统配置,自动包含所需驱动和工具:
1 | |
(2) 手动配置
通过配置文件指定额外驱动或工具:
• Debian/Ubuntu:编辑 /etc/initramfs-tools/modules:
1 | |
• RHEL/CentOS:编辑 /etc/dracut.conf 或 /etc/dracut.conf.d/*.conf:
1 | |
(3) 检查已包含的驱动
解压 initramfs 查看内容:
1 | |
4. 无 initramfs 的替代方案
若所有驱动已编译进内核(非模块化),且无需复杂存储配置,可跳过 initramfs:
(1) 内核配置
确保驱动标记为 [*](内置)而非 [M](模块):
1 | |
(2) GRUB 配置
删除 initrd 行:
1 | |
5. 调试驱动加载问题
(1) 查看内核消息
1 | |
(2) 启动时添加调试参数
在 GRUB 中编辑内核参数,添加:
1 | |
(3) 进入救援 Shell
若 initramfs 加载失败,添加 break 或 rd.break 进入调试环境:
1 | |
6. 总结
| 场景 | 驱动存放位置 | 优点 | 缺点 |
|---|---|---|---|
| 常规系统 | initramfs |
灵活支持模块化驱动和复杂配置 | 增加启动时间(约1-3秒) |
| 嵌入式/极简系统 | 内核内置(无 initramfs) |
启动更快,无额外文件 | 内核体积大,硬件适应性差 |
最佳实践:
• 服务器/桌面:使用 initramfs 自动包含驱动。
• 嵌入式设备:定制内核,将关键驱动内置以节省空间。
那grub中到底是先执行initramfs还是先执行vmlinuz?
在系统启动过程中,GRUB 会先加载 vmlinuz(内核镜像),然后加载 initramfs(初始内存文件系统)到内存,但实际执行顺序是 vmlinuz 先运行,随后由内核解压并执行 initramfs 中的初始化脚本。以下是详细流程和关键点:
1. GRUB 的加载顺序
(1) 加载阶段
• 第一步:GRUB 读取配置文件(如 /boot/grub/grub.cfg),先将 vmlinuz 加载到内存。
1 | |
• linux 指令指定内核镜像路径。
• initrd 指令指定 initramfs 路径。
• 第二步:GRUB 将 initramfs 加载到内存的指定位置(不执行它)。
(2) 移交控制权
• GRUB 通过 bootmgr 或 multiboot 协议 将控制权交给 vmlinuz,并传递以下信息:
• initramfs 在内存中的地址。
• 内核参数(如 root=/dev/nvme0n1p2)。
2. 内核(vmlinuz)的执行流程
(1) 内核初始化
• 硬件初始化:CPU、内存、基本设备(如屏幕、键盘)。
• 解压自身:vmlinuz 是压缩镜像,首先解压到内存中运行。
(2) 处理 initramfs
• **内核从 GRUB 提供的地址中读取 initramfs**,将其解压到临时内存文件系统(tmpfs)。
• 挂载为临时根文件系统:
内核将 initramfs 挂载为 /,并执行其中的初始化脚本(如 /init 或 /linuxrc)。
(3) 关键区别
• vmlinuz 是执行主体:它是内核本体,负责所有硬件初始化和后续流程。
• initramfs 是数据文件:由内核主动解压并挂载,本身不会“执行”。
3. 完整启动流程
sequenceDiagram
participant GRUB
participant vmlinuz
participant initramfs
participant RootFS
GRUB->>vmlinuz: 1. 加载内核到内存
GRUB->>initramfs: 2. 加载initramfs到内存(不执行)
vmlinuz->>initramfs: 3. 解压并挂载为临时根文件系统
initramfs->>initramfs: 4. 执行/init脚本(加载驱动、解密等)
initramfs->>RootFS: 5. 挂载真实根文件系统
initramfs->>vmlinuz: 6. 切换根目录(pivot_root)
vmlinuz->>RootFS: 7. 启动/sbin/init(systemd/sysvinit)
关键步骤解释
- GRUB 加载:
vmlinuz和initramfs被加载到内存,但只有vmlinuz被启动。 - 内核接管:
vmlinuz解压自身并初始化基础硬件。 - initramfs 处理:内核解压
initramfs并挂载为临时根文件系统。 - initramfs 脚本:执行
/init脚本,加载驱动、解密磁盘、挂载真实根文件系统。 - 根切换:通过
pivot_root切换到真实根文件系统,最终启动用户空间的init进程。
4. 常见误区澄清
(1) 不是“先执行 initramfs”
• initramfs 是数据文件,由内核主动调用,它没有独立的执行能力。
• 类比:vmlinuz 是“操作系统内核”,initramfs 是“启动时用的工具包”,内核需要工具包来完成初始化。
(2) 为什么需要 GRUB 加载 initramfs?
• 内核在启动时无法直接从磁盘读取文件(因驱动未加载),必须由引导加载程序(GRUB)提前将 initramfs 放入内存。
(3) 无 initramfs 的情况
若内核已内置所有驱动(如嵌入式系统),可直接挂载根文件系统,无需 initramfs:
1 | |
5. 调试技巧
(1) 查看 GRUB 加载信息
在 GRUB 菜单按 e 编辑条目,观察 linux 和 initrd 行:
1 | |
(2) 跟踪内核启动过程
添加内核参数 rd.debug 或 initcall_debug:
1 | |
(3) 进入 initramfs Shell
在 GRUB 中添加 break 或 rd.break:
1 | |
启动后会停在 initramfs 的调试 Shell,可手动执行 /init 或检查驱动。
6. 总结
| 阶段 | **组件 | 动作 |
|---|---|---|
| GRUB 加载 | vmlinuz |
先加载到内存(执行主体)。 |
initramfs |
后加载到内存(数据文件)。 | |
| 内核执行 | vmlinuz |
解压自身并挂载 initramfs 为临时根。 |
initramfs |
由内核调用其脚本完成早期初始化。 |
核心原则:
• vmlinuz 是执行者,initramfs 是它使用的工具包。
• GRUB 只是“搬运工”,负责将两者放入内存,不参与实际执行。
vmlinuz初始化基础硬件讲一下?
在 Linux 系统启动过程中,**vmlinuz(压缩的内核镜像)负责初始化基础硬件**,这是内核早期启动的核心阶段。以下是详细的硬件初始化流程和关键步骤:
1. vmlinuz 的硬件初始化阶段
vmlinuz 解压并运行后,硬件初始化分为多个层次,从 CPU 到存储设备逐步展开:
(1) 解压内核镜像
• vmlinuz 是经过压缩的(如 gzip 或 LZ4),首先由引导加载程序(如 GRUB)加载到内存,然后内核自解压到指定内存地址。
• 代码路径:arch/x86/boot/compressed/head_64.S(x86 架构示例)。
(2) 底层 CPU 和内存初始化
• CPU 设置:
• 激活保护模式(x86)或 EL2/EL3(ARM),启用虚拟内存(MMU)。
• 检测 CPU 特性(如 SSE、AVX、多核信息)。
• 内存管理:
• 初始化页表(paging_init),建立虚拟地址映射。
• 探测物理内存布局(通过 BIOS/UEFI 或设备树(DTB)获取)。
(3) 早期控制台和调试输出
• 初始化串口(8250 UART)、VGA 文本模式或 EFI 帧缓冲区,用于输出内核日志(printk)。
• 关键日志:
1 | |
(4) 设备树(DTB)/ACPI 解析
• ARM/嵌入式系统:加载设备树(DTB),描述硬件拓扑(如 CPU、中断控制器、时钟)。
• x86:解析 ACPI 表(如 DSDT、MADT),获取 PCI、APIC 等信息。
(5) 中断和时钟初始化
• 设置中断控制器(如 APIC、GIC),初始化时钟源(如 TSC、HPET)。
• 关键函数:
1 | |
(6) 存储设备探测
• PCI/NVMe/SATA 控制器:扫描 PCI 总线,初始化存储控制器。
1 | |
• 磁盘设备识别:读取存储设备的 Vendor/Model 信息,但此时尚未加载文件系统驱动。
2. 为什么需要 initramfs 配合?
尽管 vmlinuz 初始化了基础硬件,但以下任务需要 initramfs 辅助:
加载存储驱动模块:
• 内核可能以模块形式编译了 NVMe、RAID、LUKS 等驱动,这些模块存储在磁盘上的/lib/modules,但磁盘尚未挂载。
•initramfs提前包含这些模块,确保内核能访问根文件系统。解密和复杂存储配置:
• 加密的根文件系统(LUKS)、逻辑卷(LVM)需要用户态工具(如cryptsetup、vgchange),这些工具依赖initramfs提供的临时环境。网络根文件系统:
• 若根文件系统在 NFS/iSCSI 上,需提前加载网络驱动和配置。
3. 关键代码流程(简化版)
1 | |
4. 无 initramfs 的极简场景
若内核已内置所有必要驱动(如嵌入式系统),vmlinuz 可直接挂载根文件系统:
内核配置:
1
2
3
4Device Drivers →
[*] NVMe support (编译进内核,非模块)
File systems →
[*] Ext4 filesystem support (内置)GRUB 配置:
1
2linux /boot/vmlinuz root=/dev/nvme0n1p2 ro
# 无 initrd 指令
5. 调试硬件初始化问题
(1) 查看内核日志
1 | |
(2) 启动时观察
在 GRUB 中移除 quiet 参数,显示详细启动日志:
1 | |
(3) 使用 QEMU 调试
1 | |
6. 总结
| 阶段 | vmlinuz 的职责 |
依赖 initramfs 的场景 |
|---|---|---|
| CPU/内存 | 激活保护模式、初始化页表 | 无 |
| 控制台/中断 | 设置串口、中断控制器 | 无 |
| 存储设备探测 | 识别 PCI/NVMe/SATA 控制器 | 驱动以模块形式存在时需加载 |
| 根文件系统挂载 | 调用 mount_root() |
需要解密、LVM、网络存储等复杂配置时 |
vmlinuz 是内核的核心,负责奠定硬件运行基础,而 initramfs 是其“助手”,解决模块化和复杂配置的依赖问题。两者协作确保系统从硬件初始化平滑过渡到用户空间。
这个流程中没有体现出将initramfs解压挂成假根啊?
1 | |
您完全正确!之前的流程简化版本确实遗漏了 initramfs 解压和挂载为临时根文件系统的关键步骤。以下是 修正后的完整流程,明确标注了 initramfs 的处理位置和细节:
修正后的内核启动流程(含 initramfs 处理)
1 | |
关键步骤详解:initramfs 如何成为临时根?
1. 解压 initramfs(populate_rootfs)
• 位置:在 do_basic_setup() 阶段,内核调用 populate_rootfs()。
• 动作:
• 从内存中读取 GRUB 加载的 initramfs 镜像(CPIO 格式)。
• 解压到内核内部的 rootfs(内存文件系统)。
• 代码路径:init/initramfs.c
1 | |
2. 挂载为临时根(init_mount_tree)
• 位置:在 prepare_namespace() 中调用 init_mount_tree()。
• 动作:
• 将包含 initramfs 内容的 rootfs 挂载为 /。
• 创建必要的设备节点(如 /dev/console)。
• 效果:
1 | |
3. 执行 /init 脚本
• 位置:run_init_process("/init")。
• 动作:
• 执行 initramfs 中的 /init 脚本(通常是 BusyBox 的 shell 脚本)。
• 脚本任务:
◦ 加载存储驱动(如 modprobe nvme)。
◦ 解密 LUKS 分区或激活 LVM。
◦ 挂载真实的根文件系统到 /sysroot。
◦ 执行 pivot_root 切换到真实根。
4. 切换到真实根文件系统
• pivot_root 操作:
1 | |
对比原流程的遗漏点
| 原流程遗漏 | 修正后补充 |
|---|---|
未体现 initramfs 解压 |
明确 populate_rootfs() 和 unpack_to_rootfs() 步骤。 |
| 未展示临时根挂载 | 添加 init_mount_tree() 将 rootfs 挂载为 /。 |
未说明 /init 执行 |
补充 run_init_process("/init") 和 pivot_root 流程。 |
完整流程图
sequenceDiagram
participant GRUB
participant vmlinuz
participant initramfs
participant RootFS
GRUB->>vmlinuz: 加载内核和initramfs到内存
vmlinuz->>vmlinuz: 解压自身并初始化硬件
vmlinuz->>initramfs: populate_rootfs()解压initramfs
vmlinuz->>initramfs: init_mount_tree()挂载为临时根/
vmlinuz->>initramfs: 执行/init脚本
initramfs->>initramfs: 加载驱动、解密磁盘等
initramfs->>RootFS: 挂载真实根到/sysroot
initramfs->>RootFS: pivot_root切换根目录
RootFS->>RootFS: 启动/sbin/init(systemd)
调试技巧
1. 验证 initramfs 解压
在内核启动参数添加 rdinit=/bin/sh,进入 initramfs 的 Shell:
1 | |
检查 / 下的内容是否为 initramfs 的文件。
2. 查看内核日志
1 | |
输出示例:
1 | |
3. 手动解压 initramfs
1 | |
总结
• **vmlinuz 负责解压和挂载 initramfs**:通过 populate_rootfs() 和 init_mount_tree() 将其设为临时根。
• /init 是过渡桥梁:由 initramfs 的脚本完成硬件初始化和根切换。
• 最终目标:将控制权移交到真实根文件系统的 init 进程(如 systemd)。
这一流程确保了内核在无法直接访问磁盘时,仍能通过内存中的 initramfs 完成关键初始化!
那systemd为啥还要switch_root?到底是initramfs执行的switch_root还是systemd执行的?
在 Linux 系统启动过程中,**switch_root(或 pivot_root)操作是由 initramfs 中的初始化脚本执行的,而不是由 systemd 执行的**。这是为了完成从临时内存文件系统(initramfs)到真实根文件系统的切换。以下是详细解释:
1. 为什么需要 switch_root?
• initramfs 是临时根:
内核启动初期会将 initramfs 解压到内存并挂载为 /,但它仅包含必要的工具和驱动(如 cryptsetup、lvm2、modprobe),并非完整的操作系统环境。
• 真实根文件系统需要挂载:
真正的根文件系统(如 /dev/nvme0n1p2)可能位于加密设备、LVM 卷或网络存储上,需要 initramfs 先准备好这些环境才能挂载。
• 避免路径冲突:
若直接挂载真实根到 /,会导致 initramfs 的文件被覆盖,引发混乱。switch_root 会清空临时根,确保干净切换。
2. 谁执行 switch_root?
(1) initramfs 的初始化脚本
• 在传统的 initramfs 中,通常是一个简单的 Shell 脚本(如 /init),由 BusyBox 或类似工具解析。
• 在基于 systemd 的 initramfs 中,由 systemd 的 initramfs 版本(即 systemd-initrd)调用 switch-root 命令。
• 关键点:
• switch_root 是 initramfs 的最后一步操作,完成后才会移交控制权给真实根文件系统的 systemd。
• systemd(真实根的)此时尚未启动,因此不可能由它执行切换。
(2) systemd 在 initramfs 中的角色
• 若 initramfs 使用 systemd 作为初始化工具(如现代发行版),其流程如下:
initramfs中的systemd(systemd-initrd)负责加载驱动、解密磁盘等。- 挂载真实根文件系统到
/sysroot。 - 调用
systemd-switch-root切换到/sysroot。 - 退出并移交控制权:
initramfs的systemd进程终止,真实根的systemd(PID 1)接管。
3. 具体流程对比
(1) 传统 initramfs(BusyBox)
sequenceDiagram
participant Kernel
participant Initramfs
participant RealRoot
Kernel->>Initramfs: 解压并挂载为临时根
Initramfs->>Initramfs: 执行/init脚本(加载驱动、解密)
Initramfs->>RealRoot: 挂载真实根到/sysroot
Initramfs->>RealRoot: switch_root /sysroot /sbin/init
Initramfs-->>Kernel: 释放initramfs内存
RealRoot->>RealRoot: 启动真正的init(如systemd)
(2) 基于 systemd 的 initramfs
sequenceDiagram
participant Kernel
participant Initramfs_systemd
participant RealRoot_systemd
Kernel->>Initramfs_systemd: 解压并挂载为临时根
Initramfs_systemd->>Initramfs_systemd: systemd-initrd(加载驱动、解密)
Initramfs_systemd->>RealRoot_systemd: 挂载真实根到/sysroot
Initramfs_systemd->>RealRoot_systemd: systemd-switch-root /sysroot
Initramfs_systemd-->>Kernel: 释放initramfs内存
RealRoot_systemd->>RealRoot_systemd: 启动systemd(PID 1)
4. 关键代码和命令
(1) 传统 initramfs 的 /init 脚本
1 | |
(2) systemd 的 initramfs 操作
1 | |
5. 常见误区澄清
(1) 不是由真实根的 systemd 执行切换
• 真实根的 systemd(PID 1)尚未启动,直到 switch_root 完成后才会运行。
• initramfs 的 systemd(若有)和真实根的 systemd 是两个独立进程。
(2) switch_root vs pivot_root
• switch_root:专为 initramfs 设计,会清空临时根的所有文件。
• pivot_root:更通用的根切换机制,需手动卸载旧根(较少在 initramfs 中使用)。
6. 调试方法
(1) 观察启动日志
1 | |
(2) 进入 initramfs Shell
在 GRUB 启动菜单中编辑内核参数,添加 rd.break:
1 | |
启动后会停在 initramfs 的 Shell,可手动执行 switch_root。
7. 总结
| 阶段 | 执行者 | 操作 |
|---|---|---|
| initramfs 初始化 | initramfs 的 /init 或 systemd-initrd |
加载驱动、挂载真实根文件系统到 /sysroot。 |
| 根切换 | initramfs 的脚本或 systemd-switch-root |
调用 switch_root 切换到 /sysroot,移交控制权。 |
| 真实根启动 | 真实根的 systemd(PID 1) |
接管系统,继续启动用户空间服务。 |
核心原则:
• switch_root 是 initramfs 的“临终遗言”,完成后 initramfs 及其进程(包括自身的 systemd)会被彻底销毁。
• 真实根的 systemd 是全新的 PID 1,它从未见过 initramfs 的环境。
那这日志前面为啥标注systemd?
1 | |
您遇到的问题是 initramfs 在切换根文件系统时失败,而日志中标注 systemd 的原因在于:即使系统未成功切换到真实根文件系统,initramfs 内部可能仍在使用 systemd 作为初始化工具。以下是详细分析和解决方案:
1. 为什么日志中标注 systemd?
(1) initramfs 使用了 systemd 作为初始化工具
现代发行版(如 Ubuntu 20.04+、RHEL 8+)的 initramfs 默认集成 **systemd-initrd**(systemd 的轻量版),用于:
• 管理 initramfs 中的服务(如加载驱动、解密磁盘)。
• 尝试启动真实根文件系统的 /sbin/init(即 systemd)。
(2) 错误来源
当您使用 rd.break=pre-mount 中断启动流程时:
initramfs的systemd仍在运行(负责初始化硬件和挂载前的准备工作)。- 当它尝试执行
/sbin/init或/bin/sh时,真实根文件系统尚未挂载,因此找不到这些文件。 - 错误日志由
initramfs的systemd(PID 1)打印,而非真实根的systemd。
2. 错误日志的完整上下文
1 | |
• **systemd[1]**:表示这是 PID 1 的进程(即 initramfs 的 systemd)。
• **No /sbin/init**:initramfs 尝试启动真实根的 /sbin/init,但 /sysroot 未挂载,导致路径无效。
• **/bin/sh fallback**:systemd 的备用方案是启动 Shell,但 initramfs 中可能未包含 /bin/sh(或路径错误)。
3. 根本原因
(1) 真实根文件系统未挂载
• rd.break=pre-mount 会在挂载真实根文件系统前暂停,此时:
• /sysroot 目录为空或未挂载。
• initramfs 的 systemd 无法找到真实根中的 /sbin/init 或 /bin/sh。
(2) initramfs 的 Shell 工具缺失
• 若 initramfs 未包含 /bin/sh(或 BusyBox 未正确链接),systemd 的回退操作会失败。
4. 解决方案
(1) 手动挂载真实根文件系统
在 rd.break=pre-mount 暂停后,手动完成挂载和切换:
1 | |
(2) 修复 initramfs
若 initramfs 缺少关键工具(如 /bin/sh),需重新生成:
1 | |
(3) 使用更完整的调试 Shell
在 GRUB 中改用 rd.break(不带 pre-mount),进入 initramfs 的完整 Shell:
1 | |
5. 为什么 initramfs 的 systemd 会尝试启动 /sbin/init?
• 设计逻辑:
initramfs 的 systemd 默认假设真实根文件系统已挂载到 /sysroot,因此会尝试:
- 执行
/sysroot/sbin/init(真实根的systemd)。 - 若失败,尝试
/sysroot/bin/sh作为备用。 - 若仍失败,报错并停止(如您所见)。
• 在 rd.break=pre-mount 时的问题:
流程被中断,/sysroot 未挂载,因此所有路径解析失败。
6. 总结
| 现象 | 原因 | 解决方案 |
|---|---|---|
systemd[1] 报错 |
initramfs 的 systemd 尝试启动真实根的 /sbin/init,但 /sysroot 未挂载。 |
手动挂载 /sysroot 并执行 switch_root。 |
缺少 /bin/sh |
initramfs 未包含必要的 Shell 工具。 |
重新生成 initramfs。 |
关键点:
• **initramfs 的 systemd ≠ 真实根的 systemd**,前者是过渡工具,后者是最终目标。
• rd.break=pre-mount 是一个“半成品”状态,需手动干预完成挂载和切换。
initramfs中有systemd?
是的!现代 Linux 发行版(如 Ubuntu 20.04+、RHEL/CentOS 8+、Fedora、Arch Linux)的 initramfs 中确实会包含一个轻量版的 **systemd**(称为 systemd-initrd),用于替代传统的 Shell 脚本(如 BusyBox)来管理初始化流程。以下是关键细节:
1. 为什么 initramfs 需要 systemd?
(1) 统一初始化管理
• **传统 initramfs**:依赖 /init 脚本(通常是 BusyBox)手动加载驱动、解密磁盘等,逻辑分散且难以维护。
• **systemd-initrd**:用 systemd 的单元文件(.service)管理任务,提供依赖关系、并行启动和标准化日志。
(2) 支持复杂功能
• 自动解密:通过 systemd-cryptsetup 服务处理 LUKS 加密。
• 网络挂载根:通过 systemd-networkd 配置网络后挂载 NFS/iSCSI。
• 早期用户空间服务:如 systemd-udev 处理设备检测。
(3) 与主系统无缝衔接
• 真实根文件系统的 systemd(PID 1)可以平滑继承 initramfs 中的服务状态。
2. initramfs 中的 systemd 如何工作?
(1) 文件结构
解压 initramfs 后可以看到 systemd 相关文件:
1 | |
(2) 启动流程
- **内核加载
initramfs**:
解压后执行默认入口/init(通常是一个符号链接到/usr/lib/systemd/systemd)。 systemd作为 PID 1:
读取initramfs中的单元文件(如cryptsetup-pre.target、sysroot.mount)。- 执行任务:
• 加载驱动模块(modprobe.service)。
• 解密磁盘(systemd-cryptsetup@.service)。
• 挂载真实根文件系统到/sysroot。 - 切换根目录:
调用systemd-switch-root切换到/sysroot,启动真实根的systemd。
(3) 日志查看
1 | |
3. 与传统 initramfs(BusyBox)的对比
| 特性 | 传统 initramfs(BusyBox) |
systemd-initrd |
|---|---|---|
| 初始化逻辑 | 手动脚本(/init) |
通过 .service 单元文件管理 |
| 驱动加载 | modprobe 命令 |
systemd-modules-load.service |
| 磁盘解密 | cryptsetup 脚本 |
systemd-cryptsetup@.service |
| 日志记录 | 输出到控制台或临时文件 | 通过 journald 结构化记录 |
| 调试便利性 | 需手动添加 break 或 rd.break |
支持 systemctl 命令和日志过滤 |
4. 如何确认 initramfs 使用了 systemd?
(1) 检查 /init 的链接目标
1 | |
(2) 查看 initramfs 内容
1 | |
(3) 检查启动日志
1 | |
5. 常见问题与解决
(1) 启动卡在 initramfs 的 systemd
• 原因:真实根文件系统未挂载(如驱动缺失、解密失败)。
• 解决:
• 在 GRUB 中添加 rd.break 进入紧急 Shell。
• 手动挂载 /sysroot 并检查:
1
2
3cryptsetup open /dev/nvme0n1p2 cryptroot
mount /dev/mapper/cryptroot /sysroot
exit # 继续启动
(2) systemd 报错找不到 /sbin/init
• 原因:/sysroot 未挂载时,initramfs 的 systemd 尝试启动真实根的 systemd 失败。
• 解决:
• 确保真实根文件系统已挂载到 /sysroot。
• 检查 /sysroot/sbin/init 是否存在:
1
chroot /sysroot /bin/bash -c "ls -l /sbin/init"
(3) 禁用 initramfs 的 systemd
• 不推荐:但可通过重建 initramfs 切换回 BusyBox:
1 | |
6. 总结
• initramfs 中的 systemd 是一个轻量级版本,用于替代传统初始化脚本,提供更强大的服务管理和日志功能。
• 核心作用:
- 加载驱动、解密磁盘、挂载真实根文件系统。
- 通过
systemd-switch-root切换到真实根的systemd(PID 1)。
• 调试技巧:
• 使用journalctl查看initramfs日志。
• 通过rd.break进入紧急 Shell 手动修复。
这种设计使得现代 Linux 系统的启动流程更加统一和可靠,尤其在处理加密、网络根等复杂场景时优势明显。
initramfs中轻量级的systemd与真实的systemd的区别?initramfs中轻量级别的systemd是怎么被执行的?
在 Linux 系统启动过程中,initramfs 中的轻量级 systemd(称为 systemd-initrd)与真实根文件系统中的完整 systemd(PID 1)有显著区别。以下是它们的核心差异和执行机制的详细分析:
一、轻量级 systemd-initrd vs 完整 systemd
| 特性 | initramfs 中的 systemd-initrd |
真实根文件系统的 systemd (PID 1) |
|---|---|---|
| 二进制文件 | 精简版,仅包含必要功能(如 cryptsetup、mount) |
完整功能,支持所有系统服务管理(如 networkd、logind) |
| 服务单元 | 仅加载关键服务(如 dracut-cmdline、sysroot.mount) |
加载所有用户和系统服务(如 sshd、getty) |
| 目标(target) | 主要使用 initrd.target 和 initrd-switch-root.target |
使用标准目标(如 multi-user.target、graphical.target) |
| 日志系统 | 日志输出到内存或控制台(无持久化) | 通过 journald 持久化存储日志 |
| 设备管理 | 依赖 systemd-udevd 的轻量级版本 |
完整版 udev 规则和设备管理 |
| 依赖关系 | 仅处理与启动相关的依赖(如 cryptsetup-pre.target) |
处理所有服务的复杂依赖关系 |
| 运行环境 | 运行在 initramfs 的临时根文件系统中 |
运行在真实的根文件系统上 |
| 退出方式 | 通过 systemd-switch-root 切换到真实根后自我终止 |
持续运行,作为系统的初始化进程(PID 1) |
二、initramfs 中轻量级 systemd 的执行流程
1. 内核加载 initramfs
• GRUB 配置:
GRUB 通过 initrd 指令将 initramfs 镜像加载到内存,并传递给内核:
1 | |
• 内核解压:
内核解压 initramfs 到内存中的 tmpfs,并挂载为临时根文件系统(/)。
2. 启动轻量级 systemd
• 入口点:
initramfs 的 /init 通常是一个符号链接,指向 /usr/lib/systemd/systemd:
1 | |
• 执行流程:
- 内核调用
/init,启动systemd-initrd作为第一个用户空间进程(PID 1)。 systemd-initrd读取initramfs中的单元文件(位于/etc/systemd/system/或/usr/lib/systemd/system/)。- 依次执行关键任务:
◦ 加载内核模块(modprobe.service)。
◦ 解密磁盘(systemd-cryptsetup@.service)。
◦ 挂载真实根文件系统到/sysroot(sysroot.mount)。
◦ 准备设备节点(initrd-udevadm-cleanup-db.service)。
3. 切换到真实根文件系统
• **systemd-switch-root**:
轻量级 systemd 最终调用以下命令完成切换:
1 | |
• 操作细节:
1. 卸载 initramfs 的所有文件系统。
2. 将 /sysroot 挂载为新的根目录 /。
3. 执行真实根文件系统的 /sbin/init(即完整版 systemd)。
• 自我终止:
systemd-initrd 进程退出,由真实根的 systemd 接管 PID 1。
三、关键代码与调试技巧
1. 查看 initramfs 中的 systemd 单元
1 | |
2. 调试 systemd-initrd 启动过程
• 方法 1:添加调试参数
在 GRUB 中编辑内核命令行,添加 systemd.log_level=debug:
1 | |
• 方法 2:进入 Shell
使用 rd.break 暂停启动流程:
1 | |
进入 Shell 后手动执行:
1 | |
3. 日志分析
1 | |
四、常见问题解答
Q1: 为什么 initramfs 不直接使用完整版 systemd?
• 资源限制:initramfs 需尽量小巧(通常几 MB 到几十 MB),完整 systemd 包含大量非必要功能(如用户会话管理)。
• 单一职责:systemd-initrd 仅需完成硬件初始化和根切换,无需管理长期运行的服务。
Q2: 如何自定义 initramfs 中的 systemd 服务?
• 添加自定义单元:
在 /etc/dracut.conf.d/ 或 /usr/lib/dracut/modules.d/ 中定义新服务,然后重建 initramfs:
1 | |
Q3: 轻量级 systemd 失败后会怎样?
• Fallback 机制:
若 systemd-switch-root 失败,会尝试启动 emergency shell(若编译时启用了该选项)。
• 错误日志:
通过 journalctl 或控制台输出查看具体原因(如磁盘未解密、/sysroot 未挂载)。
五、总结
• 轻量级 systemd-initrd 是 initramfs 的“临时管理员”,专为启动初期设计,功能精简且目标明确。
• 执行流程:由内核调用 /init → 加载关键服务 → 挂载真实根 → 通过 systemd-switch-root 自我替换为完整 systemd。
• 调试核心:通过 rd.break 和 journalctl 观察初始化过程,重点关注服务依赖和根文件系统挂载步骤。
这种设计既保证了启动流程的标准化,又兼顾了灵活性和性能,是现代 Linux 系统可靠启动的关键架构!
两个systemd共用一份systemd代码?
在 Linux 系统中,**initramfs 中的轻量级 systemd(systemd-initrd)和真实根文件系统中的完整 systemd(PID 1)共享同一份源代码**,但在编译和运行时会有显著差异。以下是具体分析:
1. 代码共享:同一代码库,不同编译配置
(1) 源代码相同
• 代码来源:两者均来自 systemd 官方代码库,共用相同的核心模块(如 systemd.c、unit.c)。
• 关键区别:通过编译时的配置选项(meson 参数)和运行时环境,决定其功能范围和行为。
(2) 编译差异
| 编译选项 | systemd-initrd(initramfs) |
完整 systemd(PID 1) |
|---|---|---|
-Dinitrd=true |
启用,仅编译初始化必需的模块 | 禁用,编译全部功能模块 |
-Dstandalone-binaries=true |
生成独立二进制文件(如 systemd-udevd) |
生成完整集成二进制文件 |
-Ddefault-hierarchy=unified |
可能禁用 cgroups v2 的复杂功能 | 完全支持 cgroups v2 |
| 依赖库 | 仅链接最小依赖(如 libc、libcrypt) |
链接所有依赖(如 libselinux、dbus) |
(3) 二进制文件对比
1 | |
• **systemd-initrd**:通常更小(几百 KB 到 1 MB),仅包含必要功能。
• **完整 systemd**:更大(几 MB),包含所有功能模块。
2. 运行时差异:功能与行为
(1) 功能裁剪
• systemd-initrd 会动态禁用非必要功能:
1 | |
• 例如:跳过 user sessions、dbus、socket activation。
(2) 单元文件加载
• systemd-initrd 仅加载 initramfs 中的单元文件:
1 | |
• 完整 systemd 加载 /etc/systemd/system/ 和 /usr/lib/systemd/system/ 下的所有单元。
(3) 日志与调试
• **systemd-initrd**:日志默认输出到控制台或内存缓冲区(无持久化)。
• **完整 systemd**:通过 journald 持久化存储日志。
3. 执行流程:如何从 systemd-initrd 切换到完整 systemd?
(1) 内核启动 initramfs
- GRUB 加载
vmlinuz和initramfs到内存。 - 内核解压
initramfs,执行/init(符号链接到/usr/lib/systemd/systemd)。
(2) systemd-initrd 初始化
• 运行 initrd.target 目标,依次执行:
1 | |
(3) 切换根文件系统
调用 systemd-switch-root 完成以下操作:
- 卸载
initramfs的所有文件系统。 - 将
/sysroot挂载为新的/。 - 执行
/sbin/init(指向完整systemd),自身退出。
1 | |
4. 验证共享代码的实践方法
(1) 检查二进制文件符号
1 | |
• initramfs 的 systemd 会包含 INITRD_COMPAT 等标志。
(2) 查看版本信息
1 | |
• 两者版本号应完全一致(如 systemd 252),但功能列表不同。
(3) 调试编译过程
若自行编译 systemd,可通过 meson 参数控制:
1 | |
5. 常见问题解答
Q1: 为什么 initramfs 不直接使用完整 systemd?
• 空间限制:initramfs 需尽量小巧(通常几 MB),完整 systemd 可能占用数十 MB。
• 安全性:减少攻击面,避免在早期启动时加载非必要功能。
Q2: 能否修改 initramfs 中的 systemd 行为?
• 是:通过覆盖 initramfs 中的单元文件(如 /etc/systemd/system/initrd.target.wants/),然后重建 initramfs:
1 | |
Q3: 如果 systemd 代码更新,是否需要同步两者?
• 是:需重新编译并更新 initramfs 和真实根的 systemd 二进制文件,保持版本一致。
6. 总结
| 维度 | systemd-initrd(initramfs) |
完整 systemd(PID 1) |
|---|---|---|
| 代码来源 | 同一份源代码,编译时启用 -Dinitrd=true |
同一份源代码,默认编译 |
| 功能范围 | 仅保留硬件初始化和根切换功能 | 支持全部系统管理功能 |
| 二进制大小 | 精简(几百 KB) | 完整(几 MB) |
| 运行时行为 | 临时进程,退出后由完整 systemd 接管 |
持久运行,作为系统的初始化进程(PID 1) |
通过代码复用和编译优化,Linux 既保持了 systemd 的统一架构,又满足了启动阶段对轻量化和高效性的需求。