A33平台GDB+KGDB串口内核调试环境搭建全程指南

发布时间:2026/10/13 12:12:09
A33平台GDB+KGDB串口内核调试环境搭建全程指南 做A33平台的内核开发最痛苦的事情就是出了问题只能用printk瞎猜。之前我在一个驱动里排查偶发死锁打印点加了几十个反复编译烧录折腾了两三天才勉强定位到原因。后来把GDBKGDB这套环境搭好直接在源码级别下断点、看调用栈、查全局变量几小时的问题几分钟就能摸清。这篇文章就把我搭建这套内核调试环境的完整过程写透从方案选型、内核配置、板端启动到主机GDB接入再到我踩过的一堆坑全部记录下来。适合正在A33或其他Cortex-A7平台上做Linux驱动、内核裁剪、启动优化和崩溃分析的朋友参考。1. 方案选型为什么要折腾GDBKGDB1.1 KGDB的内核调试原理KGDB是Linux内核自带的一套远程调试机制可以把它理解为内核里内置了一个“调试代理”。目标机上运行的内核在编译时开启了KGDB支持主机端用GDB通过串口或者网线与目标机的这个代理通信。平时系统正常运行KGDB不参与任何逻辑当GDB发出断点指令或者目标机触发异常时内核会暂停执行把寄存器现场、内存数据、调用栈等通过串口回传给主机GDB。打个比方内核就像一个不停运转的车间KGDB是墙上的一扇观察窗。平时窗户关着车间里的人照常干活调试时你趴在窗口往里看不仅能看清楚机器当前的运转状态还能伸手翻看桌面上的图纸和参数。有了这扇窗排查驱动错误、分析死锁、追踪内存被谁改写这些问题就不再是盲人摸象。这套方案的核心价值在于不需要额外的高速仿真器硬件也不需要把代码下载到开发板上单步跑而是在目标机真实运行环境下直接观察和干预。对跑Linux的嵌入式平台来说这正好解决了“问题只出现在真实运行时”的痛处。1.2 与JTAG相比KGDB的取舍A33是四核Cortex-A7处理器常见的调试方案有两类一类是硬件JTAG调试器另一类就是软件KGDB。JTAG方案需要专门的调试探针价格从几千到几万不等还要在板卡上引测试点接线复杂。更麻烦的是每一块需要调试的板子都得单独接好探针产线上的问题跟进效率很低。KGDB只需要一根串口线就把运行中的内核冻结起来成本几乎为零而且对驱动开发和异常分析场景来说完全够用。当然它也有硬伤KGDB依赖中断机制工作所以在中断上下文里下断点很容易直接把系统打死内核启动极早期KGDB还没初始化完成也没法介入如果目标是调试U-Boot或者内核第一条指令前的问题KGDB同样无能为力这些场景只能上JTAG。我的选型结论很明确日常90%的内核调试场景——驱动逻辑、死锁分析、内存破坏排查、系统调用流程追踪——KGDB都够用而且上手快、成本低。只有在需要调试中断上下文、启动早期代码或者硬件异常时才需要动用JTAG。这篇文章后面所有内容都是围绕KGDB方案展开的。提示如果你要调试的是U-Boot阶段或者内核入口函数最开始的部分KGDB帮不了你不用浪费时间往下看直接考虑硬件调试器。2. 搭建前的准备硬件连接与主机环境2.1 A33平台硬件连接要点搭建这套环境之前先把硬件准备齐。你需要一块A33核心板或者开发板一个USB转串口模块一张SD卡或者网线用于加载内核镜像以及一台x86的Linux主机。A33的调试串口一般引出在UART0的测试点或排针上多数板卡电平是3.3V TTL这点务必确认。接线时只接TXD、RXD、GND三根线不要接VCC更不要把3.3V电平直接连到RS232电平的DB9接口上那样大概率会烧掉串口芯片。主机TX接板子RX主机RX接板子TX交叉连接。市面上常见的FT232和CH340模块都能用我习惯用FT232稳定性更好一些。强调一个容易被忽视的细节串口模块的电平跳线。有些模块默认输出5V TTL接到3.3V的A33串口上短期内能通但长期使用有风险而且偶尔会出现莫名奇妙的乱码。最好把跳线调到3.3V档位再上电。2.2 主机侧工具链和串口工具主机环境我用的Ubuntu 20.04 LTS64位。需要安装的软件包如下sudo apt install build-essential libncurses-dev flex bison device-tree-compiler u-boot-tools minicom picocom sudo apt install gcc-arm-linux-gnueabihf gdb-multiarch这里有一个经验之谈如果开发板BSP里带了交叉编译工具链优先用BSP自带的因为工具链版本和内核版本往往经过配套验证。用系统源里的arm-linux-gnueabihf-gcc编译多数老内核没有问题但遇到编译器优化引发的内核问题排查起来会多一层干扰。gdb方面gdb-multiarch是单二进制支持多架构的版本也可以用工具链自带的arm gdb两者功能上等价。A33是ARMv7架构后面连接时会用到set architecture arm指令。串口工具方面minicom和picocom选一个熟悉即可。我平时用minicom多一点因为它的会话配置可以保存。实际使用中注意minicom会独占串口设备后面GDB要接管同一路串口时务必先把minicom完全退出。2.3 用minicom先验证串口通信开始配置内核之前先把串口链路验证通。执行minicom -D /dev/ttyUSB0 -b 115200 -8 -N给板子上电如果串口能正常打印U-Boot启动日志说明链路没问题。如果没有输出按顺序排查先看权限执行sudo usermod -aG dialout $USER然后重新登录再看设备节点ls /dev/ttyUSB*确认识别为ttyUSB0还是ttyACM0最后检查接线交叉是否正确TXD/RXD是否接反。这一步千万别跳过。串口没有打通就去做内核配置后面出了问题根本分不清是配置问题还是硬件问题。我见过不少同事在串口没通的情况下反复改内核参数白白浪费一整天。3. 内核开启KGDB配置与编译细节3.1 需要开启的KGDB相关选项进入内核源码目录先加载平台默认配置make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- 平台默认配置名_defconfig然后打开图形化配置界面make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- menuconfig重点确认以下几个选项是否开启它们决定了后面能不能连得上、断点能不能生效、源码能不能对应上内核选项配置路径作用CONFIG_KGDByKernel hacking - KGDB: kernel debugger开启KGDB内核调试框架CONFIG_KGDB_SERIAL_CONSOLEy同上允许通过串口作为KGDB传输通道CONFIG_DEBUG_INFOyKernel hacking - Compile-time checks and compiler options - Compile the kernel with debug info生成完整的调试符号CONFIG_MAGIC_SYSRQyKernel hacking - Magic SysRq key运行时通过sysrq触发调试中断CONFIG_GDB_SCRIPTSyKernel hacking - Provide GDB scripts for kernel debugging提供lx-symbols等内核GDB辅助脚本CONFIG_DEBUG_INFO是整个调试环境的重中之重。没有调试符号GDB连上之后只能看到汇编指令无法关联到源码行和变量调试体验和写汇编没啥区别。我之前在某个BSP里漏了这个选项连接倒是正常但list命令什么都看不到排查了很久才发现是这里没勾。CONFIG_GDB_SCRIPTS也建议打开后面调试内核模块时会用到lx-symbols脚本自动装载模块符号非常方便。3.2 menuconfig实际配置步骤在menuconfig界面里按下面路径逐级展开Kernel hacking --- [*] KGDB: kernel debugger [*] KGDB: use kgdb format for console [*] Compile-time checks and compiler options --- [*] Compile the kernel with debug info [*] Magic SysRq key [*] Provide GDB scripts for kernel debugging有几个事项需要单独说明。开启KGDB后Kernel debugging菜单下会多出几个子选项其中“KGDB: use kgdb format for console”对应的就是CONFIG_KGDB_SERIAL_CONSOLE它让KGDB可以通过console所在的串口收发数据这样调试串口不用额外分配直接用现有调试串口即可。另外如果希望断点时有较好的源码级栈回溯建议一并打开CONFIG_FRAME_POINTER。这个选项在部分ARM平台上会让GDB的btbacktrace命令输出更完整、更准确。代价是函数调用时会多几条压栈指令内核体积和性能略有损失但对调试用途来说完全值得。sysrq的默认开关可能在启动参数里被关掉配置完内核后运行时可以用下面的命令临时开启echo 1 /proc/sys/kernel/sysrq如果希望每次启动都默认开启可以在内核配置里把CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE设为1或者在启动参数里加上sysrq_always_enable1。3.3 编译产物检查与vmlinux符号确认配置保存后开始编译make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- zImage dtbs -j4编译完成后除了最终的zImage和设备树dtb还要特别关注内核源码根目录下的vmlinux文件。vmlinux是带完整符号表的未压缩内核镜像GDB调试时加载的就是它zImage只是用于实际启动的压缩镜像调试时用不上。确认符号表是否完整arm-linux-gnueabihf-nm vmlinux | grep T start_kernel如果能看到start_kernel符号说明CONFIG_DEBUG_INFO生效GDB可以拿到源码级信息。如果查不到回头检查CONFIG_DEBUG_INFO是否真的选中或者编译过程是否因为某些错误跳过了调试信息生成。这一步一定要确认否则后面GDB连上后连函数名都看不到调试就无从谈起。设备树文件也要一起编译并烧录。A33平台的dts文件在arch/arm/boot/dts/目录下编译生成的dtb要和zImage配套使用。我踩过的一个坑是zImage和dtb版本不一致导致串口能打印但网卡和MMC全部初始化失败看起来像是驱动问题折腾半天才发现是设备树不匹配。4. 板端启动参数与KGDB等待连接4.1 kgdboc启动参数解析KGDB通过串口通信这个能力由内核启动参数kgdboc描述。参数的格式是kgdbocttyS0,115200这里ttyS0对应A33的UART0115200是波特率。这个参数告诉内核KGDB的调试通道使用哪个串口、以什么速率通信。一个合理的完整启动参数示例consolettyS0,115200 root/dev/mmcblk0p2 rootwait kgdbocttyS0,115200console参数指定内核控制台kgdboc参数指定调试串口这里让它们共用ttyS0。进入KGDB调试状态后console的输出会暂时冻结等你continue恢复执行后自动恢复设计上不会有冲突。如果你的板子引出了第二路串口更推荐的方式是让console和KGDB分开consolettyS0,115200 kgdbocttyS1,115200 root/dev/mmcblk0p2 rootwait这样调试内核时目标机还能在console串口上持续输出日志便于对照观察。不过前提是板子确实把第二路串口引出来了很多A33核心板只引了一路调试串口这种情况下共用ttyS0是唯一选择。4.2 使用kgdbwait调试内核早期启动如果希望在系统启动早期就停下来等GDB连接在启动参数里追加kgdbwaitconsolettyS0,115200 root/dev/mmcblk0p2 rootwait kgdbocttyS0,115200 kgdbwait加上kgdbwait后系统会在KGDB初始化完成后立刻停止等待主机GDB连接到串口。串口输出中会出现类似这样的提示KGDB: Waiting for connection from remote gdb...看到这行提示说明目标机已经完全停在等待状态。这时主机GDB就可以接管串口接下来所有的内核执行都由你控制。使用kgdbwait有一个风险如果主机GDB因为某种原因连不上系统会一直停在等待状态看起来就像启动卡死。所以只在第一次调试早期启动问题时使用日常调试建议去掉kgdbwait等系统正常启动后再手动触发。4.3 运行时通过sysrq触发调试不带kgdbwait时系统会正常启动到用户态。需要进入调试状态时在目标机串口上执行echo g /proc/sysrq-trigger这条命令会让内核立即停止进入KGDB等待状态。此时主机GDB连接即可。这个方式的好处是系统先完成所有初始化外设、文件系统、网络都就绪后再进入调试环境更接近真实运行状态排查驱动问题更有参考价值。sysrq-g的触发依赖CONFIG_MAGIC_SYSRQ编译进内核并且运行时/proc/sys/kernel/sysrq的值不为0。我习惯把启动参数写成sysrq_always_enable1省去每次手动echo的麻烦。注意执行echo g之后目标机就停了minicom会暂时没有输出这是正常的不是死机。5. 主机GDB实操连接、断点与常用技巧5.1 启动GDB并建立串口连接主机端启动GDB时加载的是vmlinux而不是zImagegdb-multiarch vmlinux进入GDB后按下面的顺序执行三条命令set architecture arm set serial baud 115200 target remote /dev/ttyUSB0第一条指定架构为ARM第二条设置串口波特率第三条建立与目标机的串口连接。连接成功后GDB会提示“Remote debugging using /dev/ttyUSB0”并停在当前内核执行位置。这里有一个典型的报错如果出现“Remote g packet reply is too long”说明架构设置不对确认set architecture arm已经执行。还有一种情况是vmlinux与目标机运行的内核不一致比如编译时间戳、配置项不同GDB连接后看到的地址和实际运行地址对不上断点自然无法命中。这种情况必须重新编译匹配的内核。另外确保此刻没有任何其他程序占用/dev/ttyUSB0特别是minicom。GDB对串口是独占访问一旦被占就报“cant open /dev/ttyUSB0”。5.2 断点设置的两种方式与适用场景KGDB支持软件断点和硬件断点两种方式。软件断点对应GDB的break命令原理是把目标地址的指令替换为陷阱指令触发异常后进入KGDB。硬件断点对应hbreak命令利用处理器自带的调试寄存器实现不修改内存指令。在内核调试中我更推荐优先使用硬件断点。原因有两个一是软件断点会修改内核代码段而代码段经常是只读映射写保护会干扰断点设置二是内核里自旋锁、信号量等并发场景非常多软件断点替换指令的瞬间存在极小的时间窗口可能引发难以复现的奇怪问题。Cortex-A7内核自带6个硬件断点寄存器日常调试完全够用。实际操作示例hbreak do_fork continue当系统执行到do_fork时自动停下GDB显示命中断点。此时可以执行bt查看调用栈info registers查看寄存器现场p/variable查看变量值x/32wx加地址查看指定内存。5.3 驱动模块调试与符号加载调试内核模块是KGDB最实用的场景。比如你写了一个驱动模块加载后系统崩溃想定位具体是哪个函数出了问题。操作流程是先在目标机上正常insmod加载模块然后到主机GDB里执行lx-symbolslx-symbols这个脚本会自动扫描目标机的/proc/modules解析已加载模块的符号表到GDB会话中。执行成功后GDB会输出类似“Loading symbols from ... /module.ko”的信息之后就可以像调试内置内核代码一样在模块函数里下断点、查看变量了。lx-symbols依赖CONFIG_GDB_SCRIPTS编译选项而且GDB必须是用vmlinux启动的。如果执行后没有任何输出先确认内核配置里CONFIG_GDB_SCRIPTSy是否编译成功以及vmlinux和模块是否用同一套工具链编译。5.4 单步执行的经验与教训GDB的next命令在KGDB场景下不太可靠。next会尝试跳过整个函数调用链而内核线程的调用关系复杂涉及中断切换、进程调度next经常在尝试跳过的过程中触发意外异常导致连接卡死。我自己用下来最稳的方式是优先使用指令级单步stepi或者先用bt理清调用关系然后手动在下一层函数入口下断点。一个典型的内核问题排查流程在问题驱动的主函数入口下硬件断点continue触发一次用info registers和bt记录现场继续continue多触发几次观察每次进入时的参数变化怀疑某个全局变量被异常修改时执行watch加变量地址设置硬件观察点变量被写入时系统立即停下这时就能看到是哪个函数、哪条指令改写了它。watch命令在内核调试中价值极高。之前排查一个数据被莫名改写的疑难问题就是靠watch全局变量地址直接抓到了元凶——一个越界写的数组。6. 常见问题排查与避坑经验汇总6.1 串口层的问题定位板子上电后串口完全没有输出这个问题排在第一位。先检查USB转串口模块被系统识别成什么设备ls /dev/ttyUSB还是ls /dev/ttyACM两种情况在gdb里配置的路径不一样。再确认电平跳线在3.3V档位以及TXD/RXD交叉正确。串口有输出但乱码通常是波特率不匹配。确认minicom和KGDB都用115200如果线材过长或者干扰大可以降到57600重新测试。我在实际项目中遇到过主机侧波特率正确但USB转串口模块晶振不准导致字符丢失的情况换一个模块就好了这类硬件问题不容易排查多准备几个模块做对照。6.2 GDB连接阶段的异常gdb报cant open /dev/ttyUSB0首先排查权限和占用。权限问题把用户加入dialout组占用问题多半是minicom没有完全退出用fuser -v /dev/ttyUSB0查看是哪个进程占着确认后kill掉。连接后出现“Remote g packet reply is too long”架构设置问题可能性最大。执行set architecture arm后再重新target remote。还有一个隐蔽原因vmlinux编译时的ARM模式配置与目标机实际运行模式不一致例如内核配置了CONFIG_THUMB2_KERNEL而GDB还在ARM模式这时需要设置set arm fallback-mode thumb。出现“Remote connection closed”这类问题一般是目标机内核主动退出了调试状态。常见原因是执行了某些KGDB不支持的操作比如在中断上下文内继续执行或者断点打在了一个内核频繁访问的路径上每次都触发断点导致系统无法继续。6.3 进入断点后的死机与多核问题断点触发后系统直接死掉这是KGDB调试中比较头疼的坑。最常见的场景是在中断上下文、自旋锁持有状态或者禁止抢占的临界区里设置了断点。KGDB本身依赖中断机制进入这些区域后再触发调试中断等于自己踩自己的脚系统直接锁死。应对方法不要在内核中断服务函数里下断点如果必须观察中断路径尝试在进入中断前的上层函数下断点或者用tracepoint替代。另一个办法是把断点打在异常返回路径上观察中断处理完成后的现场。A33是四核处理器KGDB在SMP平台上的另一个问题是一个核停下来等待调试其他核可能还在执行。这些还在运行的核会继续持有锁、修改数据导致调试现场不一致严重时直接死锁。遇到这种情况可以在KGDB的KDB子系统中把多核限制为单核调试。进入KDB后执行cpus命令查看各核状态然后按KDB语法把其他核停下来再回到GDB模式继续调试。这一步在调试并发问题时要特别注意。6.4 调试模块与符号相关的坑lx-symbols无法加载模块符号先检查vmlinux是否带完整符号以及CONFIG_GDB_SCRIPTS是否编译。另一种情况是模块重新编译过但GDB里还缓存着旧符号信息执行lx-symbols后不加任何参数先清空缓存再重新加载。还有一个我踩过多次的坑用zImage启动调试连接正常但符号地址和实际地址对不上。这通常是因为内核启用了地址随机化KASLR。遇到这种情况在启动参数里加nokaslr关闭内核地址随机化保证vmlinux中的链接地址就是实际运行地址。KGDB调试强依赖地址一致性这一步对A33平台同样有效。6.5 实用的小技巧与习惯最后分享几个我长期使用形成的习惯。调试时串口消息和KGDB共用一路时进入调试状态后console会冻结如果还需要看内核日志可以用dmesg先把日志保存到文件调试结束后再查看。GDB的日志也可以记录下来启动GDB时加--args参数配合set logging on把调试过程保存到文件方便复盘。遇到那种一加断点就复现、去掉断点就消失的诡异问题不要硬刚先怀疑KGDB本身对系统时序的影响。可以先加大断点位置与问题路径的距离或者改用KDB模式配合静态检查缩小范围后再回来继续用KGDB。调试器本身也是一个会引入干扰的工具。学会在合适的场景放弃它反而效率更高。我个人在实际操作中的体会是GDBKGDB这套环境真正解决的不是“能不能调试”的问题而是让内核调试变成一个可控、可重复、有工具支撑的过程。搭建这件事本身并不复杂难点全在细节上工具链一致、串口干净、调试信息完整、地址对应准确。这几点都注意到整条链路就会很顺。希望这篇文章能帮你少走一些我走过的弯路把宝贵的时间留在真正难啃的问题上。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询