昨天深夜调一块车载网关板,驱动里一个内存越界写得隐蔽,系统跑着跑着就僵死了。JTAG连上去看寄存器,发现MMU早就抛了异常,但串口日志早就断片了。那一刻我盯着黑屏的终端,心里明白:驱动调试这活儿,环境没搭好,等于赤手空拳上战场。

主机侧:Ubuntu工作站配置

别用Windows搞驱动开发,wsl2也不行——内核编译、NFS挂载、gdb调试,处处是坑。直接上Ubuntu 22.04 LTS,装实体机或者VMware全虚拟化(别用VirtualBox,USB透传经常抽风)。

装基础工具链这事儿,很多新手图省事只装gcc,结果编译内核时缺头文件、缺库,报错能让人查半天。直接一条命令把该装的都装上:

sudo apt install build-essential libncurses5-dev bison flex libssl-dev libelf-dev

这里libelf-dev特别重要,现在内核编译依赖它,没装的话config都过不去。

交叉编译器的选择是个关键点。以前用厂商给的编译器,结果跟glibc版本对不上,编译出的驱动加载就崩溃。现在统一用Linaro的gcc-arm-linux-gnueabihf,版本选7.5.0——别追新,太新的编译器可能跟老内核兼容性出问题。

# 解压后记得改PATH环境变量
export PATH=$PATH:/opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin

这行要写到.bashrc里,不然每次开终端都得重设。我吃过亏,编译完发现用的还是x86的gcc,白忙活半小时。

内核源码:别直接下官网的

很多人图省事,去kernel.org下最新稳定版。但实际项目用的都是芯片厂商的BSP包,里面打了一堆补丁和定制驱动。比如NXP的i.MX6系列,就得从他们的github仓库拉imx_4.19.y分支。

下载完源码第一件事是什么?先看根目录的README文件?不对,是先备份默认配置文件。

cd linux-imx
cp arch/arm/configs/imx_v7_defconfig .config

厂商的defconfig已经调好了时钟、内存映射这些板级关键参数,自己从头配大概率启动不了。

编译内核前一定执行make menuconfig,哪怕不改任何选项也要进一下——这个操作会生成隐藏的依赖文件。跳过这步直接编译,经常遇到模块编译失败。

根文件系统:别自己从头做

新手喜欢用busybox一步步构建根文件系统,勇气可嘉但没必要。现在Buildroot和Yocto已经成熟,一个命令生成全套系统。

我习惯用Buildroot,配置简单出活快。关键配置就几项:

Target Architecture -> ARM (little endian)
Target Variant -> cortex-A7
Toolchain type -> External toolchain

这里有个坑:如果选了External toolchain,上面装的Linaro编译器路径要填对。很多人这里填错,编译时又悄悄回退用内部工具链,结果二进制格式不对板子跑不起来。

文件系统格式选ext4,别用jffs2——调试阶段经常要传文件进去,ext4挂载方便。网络配置一定要开NFS,驱动调试时把模块放NFS目录里,板子直接加载,省去反复烧录的等待。

调试环境:gdb + kgdb双剑合璧

串口打印是最朴素的调试手段,但驱动里有些竞态问题,打印本身会改变时序。这时候得上kgdb。

内核配置要打开:

KGDB: Kernel hacking -> KGDB: kernel debugger
KGDB over serial (选中)

板子启动参数追加kgdboc=ttyS0,115200,串口波特率根据实际调整。主机端用gdb-multiarch连接:

gdb-multiarch vmlinux
target remote /dev/ttyUSB0

连上后就能下断点、单步跟汇编。但注意:kgdb会完全暂停CPU,多核环境下只有当前核停住,其他核继续跑,调试并发问题时要小心。

那些容易踩的坑

编译出的驱动模块版本不匹配,insmod报invalid module format。这是因为内核版本字符串里的+号——编译内核后源码树变“脏”,版本号自动加+。要么编译前先make clean,要么在.config里把CONFIG_LOCALVERSION设空。

NFS挂载失败,多半是主机防火墙没关。开发阶段直接sudo ufw disable,安全等问题调通了再说。

板子ping不通主机,检查虚拟机网络模式。用桥接模式,别用NAT——NAT模式下板子和主机不在同一网段,NFS根本走不通。

个人经验谈

驱动开发环境就像木匠的工作台,台子没搭平,再好的手艺也出不了细活。我习惯在项目开始前花半天时间把整个环境梳理一遍:编译器版本、内核分支、文件系统路径、串口权限,全部写成脚本固化下来。

别迷信“一键部署脚本”,自己亲手配一遍,每个报错都搞清楚原因,这些经验以后排查问题都能用上。环境配好后做个快照,但别依赖快照——知道怎么从零重建,才是真本事。

调试驱动最痛苦的不是代码写错,是环境问题导致的假象。系统起不来到底是内核配置不对、设备树写错、还是电源管理没初始化?有了稳定的基础环境,至少能排除工具链的干扰,直指问题核心。

记住:好用的环境不会让你工作更快,但能让你在凌晨三点半脑子发木时,少犯些愚蠢的错误。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐