2. 内核裁剪基础与调试符号:内核编译选项、Kconfig与Makefile基础、开启调试相关配置
内核裁剪并非简单的“删除代码”,而是通过精细的编译选项控制,在功能、性能与调试能力之间取得平衡。本章将深入剖析内核编译系统的核心——Kconfig与Makefile,并重点讲解如何开启调试相关的关键配置(DEBUG_INFO、FTRACE、PERF_EVENTS),为后续的故障排查工具链打下坚实基础。
2.1 内核编译选项体系:Kconfig 与 Makefile 基础
Linux 内核的构建系统由两层核心机制组成:Kconfig 负责定义和选择编译选项,Kbuild (Makefile) 负责根据选项执行编译。理解这两者的协作关系,是进行内核裁剪和调试配置的前提。
核心概念: Kconfig 定义“做什么”(哪些功能模块被编译),Makefile 定义“怎么做”(如何编译这些模块)。
2.1.1 Kconfig 语法与选项类型
Kconfig 文件定义了内核的配置菜单。每个配置选项通常以 config 关键字开头,后跟选项名(如 CONFIG_FTRACE)。以下是常见的选项类型:
| 类型 | 关键字 | 说明 | 示例 |
|---|---|---|---|
| 布尔型 | bool |
编译进内核(y)或不编译(n) | config DEBUG_INFObool "Debug info" |
| 三态型 | tristate |
编译进内核(y)、编译为模块(m)或不编译(n) | config FTRACEtristate "Tracers" |
| 整型 | int |
输入一个整数值 | config LOG_BUF_SHIFTint "Kernel log buffer size" |
| 字符串 | string |
输入一个字符串 | config CROSS_COMPILEstring "Cross-compiler tool prefix" |
Kconfig 还支持 依赖关系(depends on)和 反向依赖(select),用于构建复杂的选项逻辑树。
# 示例:FTRACE 依赖于 DEBUG_KERNEL
config FTRACE
bool "Tracers"
depends on DEBUG_KERNEL
select RING_BUFFER
help
Enable tracing of kernel events...
2.1.2 Makefile 与 Kbuild 的编译控制
当 Kconfig 生成了 .config 文件后,Makefile 通过 obj-y、obj-m 和 obj-n 变量来决定哪些文件被编译:
obj-y:无条件编译进内核。obj-$(CONFIG_XXX):根据CONFIG_XXX的值(y/m/n)决定编译方式。obj-m:编译为可加载模块。
# 内核源码树根目录 Makefile 示例片段
# 根据 CONFIG_FTRACE 的值决定是否编译 trace 目录
obj-$(CONFIG_FTRACE) += trace/
# 在 kernel/trace/Makefile 中
obj-$(CONFIG_FTRACE) += trace.o
obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
裁剪技巧: 在嵌入式系统中,通过将不需要的功能对应的
CONFIG_XXX 设为 n,可以显著减小内核体积。但调试选项(如 FTRACE)通常建议保留为 y,以便在开发阶段进行问题定位。
2.2 内核裁剪流程与 .config 文件结构
内核裁剪的典型流程如下:
- 加载默认配置:
make defconfig或make ARCH=arm multi_v7_defconfig - 交互式调整:
make menuconfig、make nconfig或make xconfig - 保存配置: 生成
.config文件 - 编译:
make -j$(nproc)
.config 文件是内核编译的“蓝图”,每一行对应一个配置选项:
# .config 文件片段
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_PERF_EVENTS=y
# CONFIG_DEBUG_FS is not set
注意: 直接手动编辑
.config 文件可能导致依赖关系不一致。推荐使用 make olddefconfig 自动处理未定义的选项,或使用 make menuconfig 进行可视化调整。
2.3 开启调试相关配置:DEBUG_INFO、FTRACE、PERF_EVENTS
在嵌入式调试场景中,以下三个配置选项是故障排查的“三驾马车”。它们分别提供符号信息、内核跟踪能力和性能事件采样能力。
2.3.1 CONFIG_DEBUG_INFO:调试符号与行号信息
该选项控制是否在生成的内核镜像和模块中包含 DWARF 调试信息(如变量名、函数原型、源码行号)。
- 开启效果:
vmlinux体积增大 2-5 倍,但objdump、gdb、addr2line等工具可以准确地将地址映射到源码位置。 - 子选项:
CONFIG_DEBUG_INFO_REDUCED:仅保留部分调试信息,减小体积。CONFIG_DEBUG_INFO_SPLIT:将调试信息分离到独立的.dwo文件中。
# 开启完整调试信息
CONFIG_DEBUG_INFO=y
# 可选:开启分离调试信息以减小内核镜像体积
CONFIG_DEBUG_INFO_SPLIT=y
实战建议: 在开发阶段始终开启
CONFIG_DEBUG_INFO。在发布阶段,可以保留 vmlinux 的调试版本,而将部署的内核镜像使用 strip 去除符号,以平衡体积与可调试性。
2.3.2 CONFIG_FTRACE:内核函数跟踪框架
Ftrace 是内核内置的跟踪器,用于记录函数调用、中断上下文、调度事件等。其核心配置如下:
| 配置项 | 类型 | 说明 |
|---|---|---|
CONFIG_FTRACE |
bool | 主开关,开启跟踪框架 |
CONFIG_FUNCTION_TRACER |
bool | 启用函数级跟踪(function 和 function_graph 跟踪器) |
CONFIG_DYNAMIC_FTRACE |
bool | 动态插桩,允许运行时启用/禁用特定函数的跟踪,减少性能开销 |
CONFIG_FTRACE_SYSCALLS |
bool | 跟踪系统调用入口和返回 |
CONFIG_STACK_TRACER |
bool | 跟踪内核栈使用情况,用于检测栈溢出 |
# 最小化 Ftrace 配置(推荐)
CONFIG_FTRACE=y
CONFIG_FUNCTION_TRACER=y
CONFIG_DYNAMIC_FTRACE=y
CONFIG_FTRACE_SYSCALLS=y
2.3.3 CONFIG_PERF_EVENTS:性能事件子系统
Perf 子系统提供硬件性能计数器(如 CPU 周期、缓存未命中)和软件事件(如上下文切换、页面错误)的采样与计数能力。
- 核心配置:
CONFIG_PERF_EVENTS=y - 硬件支持: 需要开启
CONFIG_HW_PERF_EVENTS(通常自动依赖)。 - 用户空间接口: 通过
perf_event_open()系统调用与perf命令行工具交互。
# Perf 事件配置
CONFIG_PERF_EVENTS=y
# 可选:开启跟踪点事件支持,与 Ftrace 协同工作
CONFIG_EVENT_TRACING=y
协同工作:
CONFIG_PERF_EVENTS 与 CONFIG_FTRACE 并非互斥。Perf 可以调用 Ftrace 的跟踪点(tracepoint)作为事件源,实现“perf record -e sched:sched_switch”这样的混合采样。
2.4 配置验证与常见问题
完成配置后,可以通过以下方式验证调试选项是否生效:
# 检查 .config 中相关选项
grep -E "DEBUG_INFO|FTRACE|PERF_EVENTS" .config
# 编译后检查内核是否包含调试符号
file vmlinux | grep -i "debug"
# 检查 Ftrace 是否可用
mount -t tracefs tracefs /sys/kernel/tracing
ls /sys/kernel/tracing/trace
| 常见问题 | 原因 | 解决方案 |
|---|---|---|
| 编译后内核体积过大 | DEBUG_INFO 开启导致 |
使用 DEBUG_INFO_REDUCED 或 DEBUG_INFO_SPLIT |
| Ftrace 跟踪点不可见 | 未开启 CONFIG_EVENT_TRACING |
在 menuconfig 中搜索并开启 |
| perf 命令报错“No permission” | 内核未开启 CONFIG_PERF_EVENTS |
检查 .config,重新编译内核 |
| 动态插桩不生效 | 未开启 CONFIG_DYNAMIC_FTRACE |
开启后重新编译,并确保内核命令行包含 ftrace=function |
性能影响提示: 开启
FTRACE 和 PERF_EVENTS 会引入少量运行时开销(约 1-3%)。在生产环境中,建议通过 CONFIG_DYNAMIC_FTRACE 实现按需启用,避免持续的性能损失。