3. 模拟启动故障:模拟磁盘损坏导致的启动失败

磁盘损坏是生产环境中导致系统启动失败的常见硬件故障之一。在 QEMU 环境中,我们可以通过构造损坏的磁盘镜像或修改磁盘控制器参数,精确复现“磁盘不可读”、“分区表损坏”以及“MBR 引导扇区失效”等典型场景。本节将演示如何创建故障磁盘、配置 QEMU 启动参数,并通过调试手段定位故障根因。

3.1 磁盘损坏的典型表现与分类

当磁盘出现物理或逻辑损坏时,系统启动过程会在不同阶段表现出异常。下表总结了三种常见磁盘故障类型及其在启动过程中的典型现象:

故障类型 模拟方法 启动阶段 典型错误信息
MBR 引导扇区损坏 使用 dd 清零磁盘前 512 字节 BIOS/UEFI 加载引导程序 Boot failed: not a bootable disk
分区表损坏 修改 GPT/MBR 分区表关键字段 引导加载程序读取分区表 Invalid partition table
文件系统超级块损坏 使用 dd 覆盖文件系统元数据区域 内核挂载根文件系统 VFS: Unable to mount root fs
调试要点: 在 QEMU 中模拟磁盘故障时,建议保留一份原始完好磁盘镜像的备份。通过对比正常启动与故障启动的日志输出,可以快速定位故障发生的精确阶段。

3.2 模拟 MBR 引导扇区损坏

MBR(主引导记录)位于磁盘的第一个扇区(LBA 0),包含引导代码和分区表。损坏 MBR 将导致 BIOS 无法找到可引导设备。以下是完整的模拟与调试步骤:

  1. 准备磁盘镜像: 使用 qemu-img 创建一块 1GB 的原始格式磁盘,并安装一个最小化 Linux 系统。
  2. 备份原始 MBR: dd if=disk.img of=mbr_backup.bin bs=512 count=1
  3. 损坏 MBR: dd if=/dev/zero of=disk.img bs=512 count=1 conv=notrunc
  4. 启动 QEMU 并观察故障:
qemu-system-x86_64 \
  -drive file=disk.img,format=raw,if=ide \
  -m 1024 \
  -boot order=c \
  -serial stdio \
  -d cpu_reset,int \
  -D qemu_debug.log

启动后,QEMU 控制台将输出类似以下信息:

Booting from Hard Disk...
Boot failed: not a bootable disk
No bootable device.
调试分析: 从 QEMU 的 -d cpu_reset,int 日志中可以看到,CPU 在尝试执行 MBR 代码时立即触发异常(#PF 页故障),因为前 512 字节已被清零,CPU 读取到的是全零指令(非法操作码)。通过 info registers 监控 CPU 的 CS:EIP 寄存器,可以确认 CPU 停留在 0xFFFF0(BIOS 入口)与 0x7C00(MBR 加载地址)之间循环。

3.3 模拟分区表损坏

分区表损坏通常发生在 MBR 或 GPT 头部被篡改的场景。以下示例演示如何破坏 GPT 主分区表,导致 GRUB 无法识别磁盘布局:

# 使用 hexdump 查看 GPT 头部(LBA 1)
dd if=disk.img bs=512 skip=1 count=1 | hexdump -C

# 破坏 GPT 头部的签名("EFI PART" 位于偏移 0x00)
printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
  dd of=disk.img bs=1 seek=512 conv=notrunc count=8

启动 QEMU 后,GRUB 将输出:

error: invalid signature.
Entering rescue mode...
grub rescue>
损坏区域 偏移位置 QEMU 调试方法
GPT 签名 LBA 1, 偏移 0x00 pmemsave 0x7c00 0x200 dump.bin
GPT 分区表项 LBA 2-33 info mtree -f 查看内存映射
MBR 分区表 LBA 0, 偏移 0x1BE xp /16xb 0x7c00+0x1BE

3.4 模拟文件系统超级块损坏

当内核成功加载并尝试挂载根文件系统时,如果超级块损坏,将触发内核 panic。模拟方法如下:

# 确定 ext4 超级块位置(通常位于偏移 1024 字节处)
dd if=/dev/urandom of=disk.img bs=1024 seek=1 count=1 conv=notrunc

启动 QEMU 并添加 -kernel 和 -append 参数以捕获完整内核日志:

qemu-system-x86_64 \
  -drive file=disk.img,format=raw,if=ide \
  -kernel /boot/vmlinuz \
  -append "root=/dev/sda1 console=ttyS0" \
  -serial stdio \
  -d trace:ext4_* \
  -D ext4_trace.log

内核日志将显示:

[    1.234567] EXT4-fs (sda1): bad geometry: block count exceeds size of device
[    1.234567] VFS: Cannot open root device "sda1" or unknown-block(8,1): error -5
[    1.234567] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(8,1)

3.5 磁盘故障启动流程 SVG 图

下图展示了从 BIOS 到内核挂载根文件系统的完整启动链路,并标注了三种磁盘故障的触发位置:

磁盘故障启动流程与调试介入点 磁盘故障启动流程与调试介入点 BIOS 自检 读取 MBR (LBA 0) MBR 损坏 解析分区表 分区表损坏 GRUB 加载内核 挂载根文件系统 超级块损坏 系统正常运行 调试介入点 • MBR 损坏: 检查 0x7C00 • 分区表损坏: 检查 GPT 签名 • 超级块损坏: 检查 ext4 元数据

3.6 使用 QEMU Monitor 进行故障诊断

当磁盘故障导致系统卡死或 panic 时,QEMU Monitor 是定位问题的核心工具。以下是在故障场景下的常用诊断命令:

关键命令速查表:
QEMU Monitor 命令 用途 故障场景示例
info block 查看块设备状态 确认磁盘是否被正确识别
xp /16xb 0x7c00 查看物理内存中的 MBR 内容 MBR 损坏时显示全零
info registers 查看 CPU 寄存器状态 CS:EIP 是否指向 0x7C00
gdbserver 启动 GDB 远程调试 单步跟踪引导代码执行
pmemsave 保存物理内存到文件 导出 MBR 或分区表进行分析

3.7 自动化故障注入脚本

为了在课程实验中快速切换不同故障场景,建议使用以下 Shell 脚本进行自动化故障注入:

#!/bin/bash
# disk_fault_injector.sh - 磁盘故障注入工具

DISK_IMG="disk.img"
BACKUP_DIR="./backups"

inject_mbr_fault() {
    echo "[*] 注入 MBR 损坏故障..."
    dd if=/dev/zero of=${DISK_IMG} bs=512 count=1 conv=notrunc
    echo "[+] MBR 已损坏,启动后将显示 'Boot failed'"
}

inject_gpt_fault() {
    echo "[*] 注入 GPT 分区表损坏故障..."
    printf '\x00\x00\x00\x00\x00\x00\x00\x00' | \
        dd of=${DISK_IMG} bs=1 seek=512 conv=notrunc count=8
    echo "[+] GPT 签名已清除,GRUB 将进入救援模式"
}

inject_superblock_fault() {
    echo "[*] 注入 ext4 超级块损坏故障..."
    dd if=/dev/urandom of=${DISK_IMG} bs=1024 seek=1 count=1 conv=notrunc
    echo "[+] 超级块已损坏,内核将 panic"
}

restore_disk() {
    echo "[*] 从备份恢复磁盘..."
    cp ${BACKUP_DIR}/disk_clean.img ${DISK_IMG}
    echo "[+] 磁盘已恢复"
}

case "$1" in
    mbr)    inject_mbr_fault ;;
    gpt)    inject_gpt_fault ;;
    super)  inject_superblock_fault ;;
    restore) restore_disk ;;
    *)      echo "用法: $0 {mbr|gpt|super|restore}" ;;
esac
实验注意事项:
  • 在执行故障注入前,务必使用 restore 命令恢复磁盘到初始状态,避免累积损坏导致不可预期行为。
  • 建议在 QEMU 启动参数中添加 -serial stdio 和 -d int,以捕获完整的异常中断序列。
  • 对于超级块损坏场景,内核 panic 后可使用 info qtree 查看设备树,确认磁盘控制器是否正常响应。

3.8 故障复现与调试总结

通过本节实验,我们掌握了三种磁盘损坏场景的模拟方法及其对应的调试手段。核心要点总结如下:

  • MBR 损坏 导致 BIOS 无法引导,QEMU 输出 Boot failed,通过 xp /16xb 0x7c00 可确认 MBR 内容是否被清零。
  • 分区表损坏 导致 GRUB 无法读取磁盘布局,进入救援模式,通过 pmemsave 导出 GPT 头部进行分析。
  • 超级块损坏 导致内核无法挂载根文件系统,触发 Kernel panic,通过 -d trace:ext4_* 可捕获文件系统层的详细错误。
  • QEMU Monitor 的 info block、info registers 和 gdbserver 是定位磁盘故障的三大核心工具。