4. Linux内核基础与编译
本章是进入系统级调试前的关键基石。你将理解Linux内核的源码结构、配置哲学,并以vexpress开发板为目标,完整走通从源码到可启动镜像的编译流程。掌握这些内容后,后续的QEMU加载与GDB联合调试才能有的放矢。
4.1 Linux内核简介
Linux内核是一个宏内核(Monolithic Kernel),但兼具模块化设计。它负责管理硬件资源、提供系统调用接口、调度进程与内存。在嵌入式领域,内核通常与根文件系统分离,由Bootloader(如U-Boot)加载到内存后启动。
内核源码目录结构遵循清晰的层次划分,下表列出了与编译和调试最密切的目录:
| 目录 | 作用 | 调试关注点 |
|---|---|---|
arch/ |
体系结构相关代码(arm, arm64, x86等) | 启动入口、MMU、中断向量表 |
drivers/ |
设备驱动(串口、网卡、MMC等) | 驱动初始化顺序、probe流程 |
init/ |
内核初始化(main.c) | start_kernel执行流 |
kernel/ |
核心功能(调度、irq、printk) | 进程0、1的创建 |
mm/ |
内存管理(页分配、slab) | bootmem到伙伴系统的切换 |
scripts/ |
编译与配置工具(dtc, kconfig) | 设备树编译规则 |
4.2 源码获取
推荐从kernel.org获取主线源码,或使用LTS版本以获得长期支持。以Linux 5.10 LTS为例:
# 方式一:直接下载tar包
wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.210.tar.xz
tar -xf linux-5.10.210.tar.xz
cd linux-5.10.210
# 方式二:使用git克隆(推荐,便于切换分支和打补丁)
git clone --depth=1 -b v5.10 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
4.3 内核配置:defconfig与menuconfig
内核配置是编译前最关键的一步。配置系统基于Kconfig语言,提供两种主要方式:
- defconfig(默认配置): 每个架构/平台提供一份最小可用配置,位于
arch/<arch>/configs/下。例如vexpress的默认配置为vexpress_defconfig。 - menuconfig(交互式配置): 基于ncurses的图形界面,用于微调内核功能、开启调试选项。
配置流程如下:
# 第一步:加载vexpress默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
# 第二步:交互式微调(可选,但强烈建议开启调试符号)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
在menuconfig中,务必确认以下调试相关选项:
| 配置项 | 路径 | 说明 |
|---|---|---|
CONFIG_DEBUG_INFO |
Kernel hacking → Compile-time checks | 生成DWARF调试符号(vmlinux必备) |
CONFIG_GDB_SCRIPTS |
Kernel hacking → GDB scripts | 提供lx-symbols等辅助调试命令 |
CONFIG_EARLY_PRINTK |
Kernel hacking → Early printk | 早期串口输出,便于跟踪启动崩溃 |
CONFIG_DEBUG_LL |
Kernel hacking → Low-level debug | 低级别调试,需配合具体UART地址 |
/ 键可搜索配置项。例如输入 DEBUG_INFO 可直接定位,避免手动翻找。
4.4 编译流程(以vexpress架构为例)
vexpress是ARM Versatile Express系列开发板的QEMU模拟目标,使用Cortex-A9 MPCore处理器。编译命令如下:
# 设置环境变量(也可直接写在make参数中)
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
# 编译内核镜像与设备树
make -j$(nproc) zImage
make -j$(nproc) dtbs
# 编译内核模块(可选,后续可放入根文件系统)
make -j$(nproc) modules
编译完成后,关键文件位于以下路径:
| 文件 | 路径 | 格式 | 用途 |
|---|---|---|---|
vmlinux |
源码根目录 | ELF(未压缩,含符号) | GDB调试目标文件 |
zImage |
arch/arm/boot/ |
自解压二进制 | QEMU加载的内核镜像 |
vexpress-v2p-ca9.dtb |
arch/arm/boot/dts/ |
扁平设备树二进制 | 描述vexpress板级硬件信息 |
make -j1 观察完整输出,确认无误后再使用 -j$(nproc) 并行编译。如果修改了设备树源文件(.dts),只需重新执行 make dtbs,无需重编整个内核。
4.5 生成的关键文件详解
理解三个关键文件的生成链路与内部结构,是后续调试的基础。下图展示了从源码到最终镜像的编译流程:
4.5.1 vmlinux
vmlinux是编译链接后生成的原始ELF文件,包含完整的符号表、调试信息(如果开启了CONFIG_DEBUG_INFO)。它是GDB调试的“地图”,但体积较大(通常几十MB),不适合直接用于启动。
# 查看vmlinux的段信息
arm-linux-gnueabihf-objdump -h vmlinux | head -20
# 查看内核符号表(可用于地址回溯)
arm-linux-gnueabihf-nm vmlinux | grep start_kernel
4.5.2 zImage
zImage由vmlinux经过objcopy去除ELF头部、再经过gzip压缩,并在头部附加自解压代码(decompress_kernel)生成。QEMU加载zImage后,CPU首先执行解压代码,将内核解压到指定地址,然后跳转到解压后的入口。
# 查看zImage的入口地址(前64字节包含解压头)
hexdump -C arch/arm/boot/zImage | head -4
4.5.3 设备树(DTB)
设备树是描述板级硬件信息的结构化数据,替代了旧版内核中的大量板级文件(board-*.c)。vexpress的设备树源文件位于 arch/arm/boot/dts/vexpress-v2p-ca9.dts,编译后生成dtb。
# 反编译dtb查看硬件拓扑
arm-linux-gnueabihf-objdump -d arch/arm/boot/dts/vexpress-v2p-ca9.dtb > dtb_dis.S
# 或使用dtc工具直接查看
dtc -I dtb -O dts arch/arm/boot/dts/vexpress-v2p-ca9.dtb | head -50
-serial 参数。
4.6 验证编译产物
编译完成后,建议执行以下快速验证:
# 检查关键文件是否存在
ls -lh vmlinux
ls -lh arch/arm/boot/zImage
ls -lh arch/arm/boot/dts/vexpress-v2p-ca9.dtb
# 检查vmlinux是否包含调试符号
arm-linux-gnueabihf-readelf -S vmlinux | grep debug
# 检查zImage是否为有效ARM镜像(前4字节应为魔数)
hexdump -C arch/arm/boot/zImage | head -1
如果 readelf 输出中包含 .debug_info、.debug_line 等段,说明调试符号已正确生成,后续GDB可以正常解析源码行号与变量。
make dtbs 而非仅 make zImage。设备树编译与内核镜像编译是独立的make目标。