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 后,调试符号表在内核编译流水线中的产生与传递路径:

DEBUG_INFO 符号表膨胀路径 DEBUG_INFO 符号表膨胀路径 *.c 源文件 GCC -g 编译 *.o + 符号表 LD 链接 vmlinux + 符号 strip / objcopy zImage / uImage 关闭 DEBUG_INFO 后,GCC 不生成 .debug_* 段,.o 文件体积减少 60%–80%,链接与 strip 阶段耗时大幅降低
优化原理: 调试符号表主要存储在 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 段的对比:

DEBUG_INFO 开启/关闭 ELF 段对比 DEBUG_INFO 开启 / 关闭 ELF 段对比 开启 DEBUG_INFO .text + .data + .bss (38 MB) .debug_info + .debug_abbrev + .debug_line (854 MB) 关闭 DEBUG_INFO .text + .data + .bss (38 MB) 调试段完全消失 (0 MB) 关闭后节省 854 MB 关键结论 关闭 DEBUG_INFO 后,调试段完全消失,vmlinux 体积从 892 MB 降至 38 MB 编译时间减少 55% 以上,磁盘写入量减少 80% 以上

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 快速关闭。