Linux内核启动异常定位与QEMU调试实践 - 第1节

1.1 Linux 内核启动流程概述

Linux 内核的启动是一个从硬件复位到用户空间 init 进程的完整链条。理解这一流程是定位启动异常的前提。整个启动过程可划分为以下五个核心阶段:

核心认知: 启动异常通常发生在阶段边界(如解压完成、根文件系统挂载),掌握每个阶段的入口函数与关键日志,能帮助开发者快速缩小问题范围。

1.1.1 启动阶段划分

阶段 入口/关键点 主要工作 典型异常表现
① Bootloader U-Boot / GRUB 硬件初始化、加载内核映像到内存、传递设备树(DTB)与启动参数 串口无输出、CRC错误、DTB加载失败
② 内核解压 decompress_kernel() 自解压压缩内核(zImage/bzImage),跳转到解压后入口 解压卡死、CRC mismatch、内存不足
③ 汇编启动 stext / _start 设置页表、使能MMU、切换至C环境、调用start_kernel MMU异常、未定义指令、页表错误
④ 内核初始化 start_kernel() 调度器、内存管理、中断、驱动等子系统初始化 NULL指针、Oops、panic、驱动probe失败
⑤ 用户空间启动 init 进程 挂载根文件系统、执行/sbin/init,启动系统服务 Kernel panic - not syncing、无法挂载rootfs

1.1.2 启动流程可视化

下图展示了从 Bootloader 到用户空间 init 的完整数据流与控制流:

Linux 内核启动全流程 ① Bootloader U-Boot / GRUB ② 内核解压 decompress_kernel() ③ 汇编启动 stext / _start ④ 内核初始化 start_kernel() 调度/内存/中断/驱动 ⑤ 用户空间启动 init 进程 挂载rootfs / 启动服务 🔍 调试关键点 • 阶段①→②:检查 Bootloader 是否正确传递 DTB 与 kernel 地址 • 阶段②→③:解压完成后若卡死,优先检查内存布局与 MMU 页表 • 阶段③→④:start_kernel 前出现异常,需用汇编级调试器(如 QEMU + GDB) • 阶段④→⑤:Kernel panic - not syncing 通常与 rootfs 或驱动有关
调试提示: 在 QEMU 中,可以通过 -append "earlyprintk" 或 -append "loglevel=8" 提前看到内核早期打印信息,这对定位汇编阶段到 start_kernel 之间的异常非常关键。

1.2 QEMU 简介

QEMU(Quick EMUlator)是一款支持全系统仿真与用户态仿真的开源虚拟化工具。在本课程中,QEMU 将作为内核调试的核心平台,提供以下关键能力:

  • 全系统仿真: 模拟完整的 ARM、RISC-V、x86 等硬件平台,无需真实开发板即可运行与调试内核。
  • GDB 调试支持: 通过 -s -S 参数暂停 CPU 并等待 GDB 连接,实现源码级单步调试。
  • 快照与回放: 支持 savevm/loadvm 保存和恢复虚拟机状态,便于复现启动异常。
  • 虚拟设备模拟: 提供 virtio、PCIe、UART、SDHCI 等标准设备模型,驱动开发与调试无需硬件。

1.2.1 QEMU 调试模式对比

模式 启动参数 适用场景 调试能力
普通运行 qemu-system-arm ... 功能验证、启动测试 仅串口日志
GDB 调试 -s -S 内核早期启动、汇编级异常 断点、单步、查看寄存器/内存
QMP 监控 -qmp tcp:localhost:4444,server 自动化测试、状态查询 查询设备状态、注入事件
Trace 模式 -trace events=events.txt 性能分析、驱动行为追踪 记录函数调用、内存访问

1.3 实验环境搭建

本节将指导你完成 QEMU、交叉编译工具链与 Linux 内核源码的安装配置。所有操作基于 Ubuntu 22.04 LTS 环境,ARM64(aarch64)为目标架构。

1.3.1 安装 QEMU

QEMU 建议通过源码编译安装,以获得最新特性与调试支持。以下为安装步骤:

  1. 安装依赖包:
sudo apt update
sudo apt install -y git build-essential ninja-build pkg-config libglib2.0-dev libpixman-1-dev libslirp-dev
  1. 下载并编译 QEMU 8.2+:
wget https://download.qemu.org/qemu-8.2.0.tar.xz
tar xf qemu-8.2.0.tar.xz
cd qemu-8.2.0
./configure --target-list=aarch64-softmmu,arm-softmmu --enable-debug --enable-slirp
make -j$(nproc)
sudo make install
  1. 验证安装:
qemu-system-aarch64 --version
# 输出示例:QEMU emulator version 8.2.0
注意: --enable-debug 选项会编译 QEMU 的调试符号,增加编译时间但显著提升 GDB 调试 QEMU 自身的能力。如果磁盘空间紧张,可以去掉此选项。

1.3.2 安装交叉编译工具链

交叉编译工具链用于在 x86 主机上编译 ARM64 架构的内核与驱动。推荐使用官方 gcc-arm-10.3 工具链:

wget https://developer.arm.com/-/media/Files/downloads/gnu/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
tar xf gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu.tar.xz
sudo mv gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu /opt/
export PATH=/opt/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin:$PATH

验证工具链:

aarch64-none-linux-gnu-gcc --version
# 输出示例:aarch64-none-linux-gnu-gcc (GNU Toolchain) 10.3.1 20210621
建议: 将 export PATH=... 添加到 ~/.bashrc 中,避免每次打开终端都需要手动设置。

1.3.3 获取与配置 Linux 内核源码

本课程使用 Linux 6.6 LTS 版本,该版本对 QEMU virt 平台支持完善,且包含最新的调试特性:

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.8.tar.xz
tar xf linux-6.6.8.tar.xz
cd linux-6.6.8

生成默认的 QEMU ARM64 配置:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- defconfig
# 额外开启调试选项
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- menuconfig

在 menuconfig 中确保以下选项开启:

  • Kernel hacking → Kernel debugging
  • Kernel hacking → Compile-time checks and compiler options → Provide GDB scripts for kernel debugging
  • Kernel hacking → Magic SysRq key
  • Device Drivers → Character devices → Serial drivers → ARM AMBA PL011 serial port support

编译内核:

make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -j$(nproc) Image.gz
# 编译设备树
make ARCH=arm64 CROSS_COMPILE=aarch64-none-linux-gnu- -j$(nproc) dtbs

编译完成后,内核映像位于 arch/arm64/boot/Image.gz,设备树位于 arch/arm64/boot/dts/qemu/qemu-virt.dtb。

1.3.4 环境结构总览

下图展示了实验环境中各组件之间的依赖与数据流关系:

实验环境组件关系 主机 (x86_64) Ubuntu 22.04 LTS 交叉编译工具链 QEMU 8.2.0 交叉编译工具链 aarch64-none-linux-gnu- GCC 10.3 / Binutils 编译内核 + 驱动 Linux 内核源码 v6.6.8 LTS Image.gz + DTB 调试符号 (vmlinux) QEMU 虚拟机 (aarch64) virt 平台 / 4核CPU / 2GB RAM / virtio 设备 运行编译好的内核映像与根文件系统 💡 工作流:主机上使用交叉工具链编译内核 → QEMU 加载内核映像与 DTB → 启动后通过 GDB 远程调试

1.3.5 快速验证:启动最小内核

使用以下命令启动编译好的内核,验证环境是否正常工作:

qemu-system-aarch64 \
  -machine virt \
  -cpu cortex-a72 \
  -smp 4 \
  -m 2G \
  -kernel arch/arm64/boot/Image.gz \
  -dtb arch/arm64/boot/dts/qemu/qemu-virt.dtb \
  -append "console=ttyAMA0 root=/dev/vda rw" \
  -drive file=rootfs.ext4,if=virtio,format=raw \
  -nographic
预期结果: 串口输出内核启动日志,最终出现 Welcome to Buildroot 或 login: 提示符。如果卡在 Kernel panic - not syncing: VFS: Unable to mount root fs,说明根文件系统未正确配置,后续章节将详细讲解如何制作 rootfs。

1.4 本章小结

本章完成了以下三个核心目标:

  • 理解启动流程: 掌握了从 Bootloader 到用户空间的五个阶段及其关键入口函数。
  • 认识 QEMU 调试能力: 了解了 QEMU 的四种调试模式及其适用场景。
  • 搭建实验环境: 成功安装 QEMU 8.2、ARM64 交叉编译工具链、Linux 6.6 内核源码,并验证了最小启动。