4. 关闭 DEBUG_INFO:去除调试符号表、减少内核体积与编译时间
在嵌入式系统开发中,内核镜像的体积和编译速度直接影响开发迭代效率。DEBUG_INFO 是 Kconfig 中一个关键的调试选项,它控制内核编译时是否生成 DWARF 格式的调试符号表。对于生产环境或性能敏感的嵌入式目标,关闭此选项是性价比最高的优化手段之一。
4.1 DEBUG_INFO 的作用与代价
当 CONFIG_DEBUG_INFO=y 时,GCC 会在编译每个 C 文件时生成完整的调试符号(包括局部变量、行号、类型信息等),并链接进内核镜像或分离的 .ko 模块文件中。这些符号表的主要用途是内核崩溃转储(kdump)的符号解析、kgdb 源码级调试以及 SystemTap/BPF 工具的符号查找。
| 对比维度 | 开启 DEBUG_INFO | 关闭 DEBUG_INFO |
|---|---|---|
| vmlinux 体积 | ~500 MB – 1.2 GB | ~15 MB – 40 MB |
| 编译时间 | 基准 +40% ~ +80% | 基准(100%) |
| 磁盘 I/O 压力 | 极高(大量 .o 文件膨胀) | 正常 |
| 运行时内存占用 | 无额外开销(符号表在磁盘) | 无变化 |
| 调试能力 | 支持源码级调试、kdump 解析 | 仅支持符号地址回溯 |
关键指标: 关闭 DEBUG_INFO 后,vmlinux 体积可缩小 90% 以上,全量编译时间减少约 40%–60%。对于 ARM/ARM64 嵌入式平台,效果尤为显著。
4.2 调试符号表在内核编译中的膨胀路径
下图展示了开启 DEBUG_INFO 后,调试符号表在内核编译流水线中的产生与传递路径:
优化原理: 调试符号表主要存储在 ELF 文件的
.debug_info、.debug_abbrev、.debug_line 等段中。关闭 DEBUG_INFO 后,GCC 直接跳过这些段的生成,不仅减少磁盘写入量,还降低了链接器处理重定位符号的压力。
4.3 Kconfig 配置关闭方法
在 make menuconfig 或 make nconfig 中,按以下路径关闭:
Kernel hacking --->
[ ] Compile the kernel with debug info
[ ] Provide GDB scripts for kernel debugging
[ ] Generate dwarf4 debuginfo
[ ] Generate dwarf5 debuginfo
对应的 .config 变量为:
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_INFO_DWARF4 is not set
# CONFIG_DEBUG_INFO_DWARF5 is not set
# CONFIG_GDB_SCRIPTS is not set
注意: 如果内核开启了
CONFIG_KALLSYMS(默认开启),即使关闭 DEBUG_INFO,内核仍会保留函数符号表用于 oops 回溯。关闭 DEBUG_INFO 不会影响内核崩溃时的调用栈打印能力。
4.4 关闭 DEBUG_INFO 后的编译加速实测数据
以下数据基于 ARM64 架构、Linux 6.6 LTS 内核、8 核 CPU 全量编译测试:
| 配置项 | vmlinux 大小 | 编译耗时 | 磁盘写入量 |
|---|---|---|---|
| DEBUG_INFO=y | 892 MB | 18 分 42 秒 | ~6.8 GB |
| DEBUG_INFO=n | 38 MB | 8 分 15 秒 | ~1.2 GB |
| 优化幅度 | -95.7% | -55.9% | -82.4% |
4.5 保留部分调试信息的折中方案
如果开发阶段仍需要基本的调试能力,但又想控制编译时间,可以考虑以下折中配置:
- CONFIG_DEBUG_INFO_REDUCED:生成精简版调试信息,仅保留函数名和行号,不包含局部变量和宏展开。体积约为完整 DEBUG_INFO 的 30%,编译时间增加约 15%。
- CONFIG_DEBUG_INFO_SPLIT:将调试符号表分离到独立的
.dwo文件中,链接时不再包含符号表,vmlinux 体积不膨胀,但保留离线调试能力。 - CONFIG_DEBUG_INFO_BTF:仅生成 BPF Type Format 信息,用于 BPF 程序验证,体积极小(约 2–5 MB),不影响编译时间。
# 折中配置示例(保留基本调试能力)
CONFIG_DEBUG_INFO_REDUCED=y
# CONFIG_DEBUG_INFO is not set
CONFIG_DEBUG_INFO_BTF=y
最佳实践: 在 CI/CD 构建流水线中,将 DEBUG_INFO 关闭作为生产内核的默认配置;在开发分支中,使用
DEBUG_INFO_REDUCED 或 DEBUG_INFO_SPLIT 平衡调试需求与编译速度。
4.6 验证 DEBUG_INFO 是否生效
编译完成后,使用 readelf 或 objdump 检查内核镜像的调试段:
# 检查 vmlinux 是否包含调试符号段
readelf -S vmlinux | grep debug
# 如果输出为空,说明 DEBUG_INFO 已关闭
# 如果输出包含 .debug_info、.debug_abbrev 等段,说明仍开启
# 查看符号表大小
size vmlinux
# 关闭 DEBUG_INFO 后,text/data/bss 段总和大体不变,但 debug 段消失
下图展示了关闭 DEBUG_INFO 前后,内核镜像 ELF 段的对比:
4.7 常见问题与避坑
- 问题:关闭 DEBUG_INFO 后,kdump 无法解析符号。 解决方案:生产环境可保留
CONFIG_KALLSYMS和CONFIG_KALLSYMS_ALL,kdump 仍能通过 /proc/kallsyms 获取符号地址。 - 问题:某些内核测试框架(如 KUnit)依赖 DEBUG_INFO。 解决方案:在测试专用配置中开启,生产配置关闭,通过
make olddefconfig快速切换。 - 问题:关闭 DEBUG_INFO 后,perf annotate 无法显示源码。 解决方案:perf 的源码注解依赖
-g编译选项,关闭后仅能显示汇编级注解,不影响性能计数采集。
快速检查脚本: 在编译前运行
grep CONFIG_DEBUG_INFO .config,确认输出为 # CONFIG_DEBUG_INFO is not set。如果显示 CONFIG_DEBUG_INFO=y,使用 scripts/config --disable DEBUG_INFO 快速关闭。