错误排查指南
目标读者:所有用户
难度级别:🟡 中级
包含内容:常见错误、解决方案、调试技巧、日志分析
📋 目录
🔧 编译错误
错误类型1:内核未配置
错误现象:
bash
========================================
❌ 内核未正确编译
========================================
内核类型: 主线内核 (linux_mainline)
内核目录: /path/to/linux_mainline
输出目录: /path/to/out/mainline/linux
缺少以下文件:
- 内核配置文件: .config
- autoconf.h
- Module.symvers (需要运行 modules_prepare)可能原因:
- 首次使用,内核源码未配置
- 内核输出目录被清理
- 切换了内核类型但未重新配置
解决步骤:
- 检查内核源码是否存在
bash
ls -la third_party/linux-mainline/
# 应该看到很多文件和目录- 配置内核
bash
cd third_party/linux-mainline/
# 使用默认配置
make O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
imx_aes_mainline_defconfig
# 或者对于imx内核
make O=../../out/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
imx_aes_defconfig- 准备内核模块
bash
# 快速准备(推荐)
make O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
modules_prepare
# 或者完整编译(耗时较长)
make O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
-j$(nproc)预防措施:
- ✅ 首次使用前,按照快速开始指南配置内核
- ✅ 避免清理
out/目录中的内核配置文件 - ✅ 切换内核类型后,重新配置和编译
错误类型2:交叉编译工具链未找到
错误现象:
bash
arm-none-linux-gnueabihf-gcc: command not found
make: arm-none-linux-gnueabihf-gcc:命令未找到可能原因:
- 交叉编译工具链未安装
- 工具链未添加到PATH环境变量
- 工具链安装路径不正确
解决步骤:
- 检查工具链是否存在
bash
# 查找工具链
find /usr -name "*arm-none-linux-gnueabihf-gcc" 2>/dev/null
find ~/opt -name "*arm-none-linux-gnueabihf-gcc" 2>/dev/null
# 或者
which arm-none-linux-gnueabihf-gcc- 安装工具链
Ubuntu/Debian:
bash
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihfFedora/RHEL:
bash
sudo dnf install arm-linux-gnueabihf-gcc- 添加到PATH
bash
# 临时添加(当前会话有效)
export PATH=/path/to/toolchain/bin:$PATH
# 永久添加(添加到~/.bashrc或~/.zshrc)
echo 'export PATH=/path/to/toolchain/bin:$PATH' >> ~/.bashrc
source ~/.bashrc- 验证安装
bash
arm-none-linux-gnueabihf-gcc --version
# 应该显示版本信息预防措施:
- ✅ 使用环境管理脚本自动配置工具链路径
- ✅ 在项目的环境初始化脚本中设置PATH
- ✅ 文档中明确说明工具链要求
错误类型3:驱动源码语法错误
错误现象:
bash
fake_driver.c:25:2: error: expected ';' before 'return'
return 0;
^可能原因:
- C代码语法错误
- 缺少必要的头文件
- 内核API使用错误
解决步骤:
- 查看完整错误信息
bash
# 重新编译,查看完整错误
./scripts/driver_helper/build_driver.sh example-driver 2>&1 | tee build.log- 定位错误行
bash
# 查看错误行附近的代码
sed -n '20,30p' driver/example-driver/alpha-board/fake_driver.c- 修正错误
c
// 错误示例
static int __init fake_init(void)
{
pr_info("Init\n")
return 0; // ← 上一行缺少分号
}
// 正确示例
static int __init fake_init(void)
{
pr_info("Init\n"); // ← 添加分号
return 0;
}- 使用代码检查工具
bash
# 使用sparse进行静态检查
cd third_party/linux-mainline/
make C=2 CF="-D__CHECK_ENDIAN__" \
O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
M=../../driver/example-driver/alpha-board/ modules预防措施:
- ✅ 使用支持语法检查的编辑器(VSCode、vim等)
- ✅ 提交代码前进行编译测试
- ✅ 使用内核的编码风格检查脚本
错误类型4:设备树编译错误
错误现象:
bash
Error: .../imx6ull-aes-example-driver.dts:10.23-24 syntax error
FATAL ERROR: Unable to parse input tree可能原因:
- 设备树语法错误
- 节点名称格式错误
- 引用的节点不存在
解决步骤:
- 手动编译查看详细错误
bash
cd driver/device_tree/alpha-board/example-driver/
# 预处理
gcc -E -nostdinc -P -x assembler-with-cpp \
-I ../../../third_party/linux-mainline/arch/arm/boot/dts \
-I ../../../third_party/linux-mainline/arch/arm/boot/dts/nxp/imx \
-I ../../../third_party/linux-mainline/include \
-I ./linux \
-undef -D__DTS__ \
-o /tmp/test.dts imx6ull-aes-example-driver.dts
# 编译
dtc -I dts -O dtb -o /tmp/test.dtb /tmp/test.dts- 常见语法错误和修正
c
// 错误1:节点名称格式错误
/fake-device { // ← 错误:缺少@
compatible = "fake";
};
// 正确1:节点名称格式
fake-device { // ← 正确:根节点下的子节点不需要@
compatible = "fake";
};
// 错误2:节点地址格式错误
fake-i2c@5x0 { // ← 错误:地址格式不对
reg = <0x50>;
};
// 正确2:节点地址格式
fake-i2c@50 { // ← 正确:使用正确的十六进制格式
reg = <0x50>;
};
// 错误3:缺少分号
node {
compatible = "fake" // ← 缺少分号
}
// 正确3:添加分号
node {
compatible = "fake"; // ← 添加分号
};- 使用设备树编译器检查
bash
# 只检查语法,不生成文件
dtc -I dts -O dtb -o /dev/null imx6ull-aes-example-driver.dts预防措施:
- ✅ 使用支持设备树语法高亮的编辑器
- ✅ 参考内核文档中的设备树规范
- ✅ 编译前先进行语法检查
🚚 部署错误
错误类型1:目标目录不存在
错误现象:
bash
[ERROR] 目录不存在: /srv/tftp可能原因:
- TFTP服务器未配置
- NFS目录未创建
- 配置文件中的路径不正确
解决步骤:
- 检查配置文件
bash
cat scripts/driver_helper/driver_helper.conf
# 查看路径配置
# TFTP_DIR="${HOME}/tftp"
# NFS_DIR="rootfs/nfs"- 创建目标目录
bash
# 创建TFTP目录
mkdir -p ~/tftp
# 创建NFS目录
mkdir -p rootfs/nfs- 更新配置文件
bash
# 编辑配置文件
vim scripts/driver_helper/driver_helper.conf
# 修改路径
TFTP_DIR="/你的实际/tftp/路径"
NFS_DIR="/你的实际/nfs/路径"- 配置TFTP服务器(如果需要)
bash
# 安装TFTP服务器
sudo apt-get install tftpd-hpa
# 配置TFTP
sudo vim /etc/default/tftpd-hpa
# 修改TFTP_DIRECTORY为你的路径
# 重启服务
sudo systemctl restart tftpd-hpa预防措施:
- ✅ 首次使用前,先配置好部署目标
- ✅ 在项目README中明确说明部署路径要求
- ✅ 提供自动化配置脚本
错误类型2:权限不足
错误现象:
bash
cp: 无法创建普通文件'/srv/tftp/imx6ull-aes.dtb': 权限不够可能原因:
- 目标目录需要root权限
- 当前用户不在正确的组中
- 文件系统权限设置不当
解决步骤:
- 使用sudo部署
bash
# 方法1:使用sudo
sudo ./scripts/driver_helper/deploy_driver.sh \
out/driver_artifacts/example-driver/alpha-board \
--target=tftp- 修改目录权限
bash
# 修改目录所有者
sudo chown -R $USER:$USER ~/tftp
sudo chown -R $USER:$USER rootfs/nfs
# 或者添加写权限
chmod o+w /srv/tftp- 将用户添加到正确的组
bash
# 将用户添加到tftp组(如果存在)
sudo usermod -a -G tftp $USER
# 重新登录以使组权限生效预防措施:
- ✅ 在用户目录下创建部署目录(避免权限问题)
- ✅ 使用适当的组权限设置
- ✅ 在配置脚本中检查并设置权限
错误类型3:SSH连接失败
错误现象:
bash
[ERROR] 无法连接到 user@remote-host
ssh: connect to host remote-host port 22: Connection refused可能原因:
- 远程主机未启动
- SSH服务未运行
- 网络连接问题
- 主机名或端口配置错误
解决步骤:
- 测试网络连接
bash
# 测试主机是否可达
ping remote-host
# 测试SSH端口
telnet remote-host 22
nc -zv remote-host 22- 检查SSH配置
bash
# 检查本地SSH配置
cat ~/.ssh/config
# 测试SSH连接
ssh -v user@remote-host- 配置SSH密钥认证
bash
# 生成SSH密钥(如果还没有)
ssh-keygen -t rsa -b 4096
# 复制公钥到远程主机
ssh-copy-id user@remote-host
# 测试免密登录
ssh user@remote-host- 检查远程主机SSH服务
bash
# 在远程主机上
sudo systemctl status ssh
sudo systemctl start ssh预防措施:
- ✅ 使用SSH配置文件管理主机信息
- ✅ 配置SSH密钥认证,避免密码输入
- ✅ 在部署脚本中添加连接超时和重试机制
💾 加载错误
错误类型1:内核版本不匹配
错误现象:
bash
insmod: ERROR: could not insert module fake_driver.ko: Invalid module format
# 或
dmesg显示: version magic 'X.Y.Z' should be 'A.B.C'可能原因:
- 驱动编译时使用的内核与运行时内核不同
- 内核配置不匹配
解决步骤:
- 检查内核版本
bash
# 在目标板上检查运行时内核版本
uname -r
# 在开发机上检查驱动编译的内核版本
modinfo fake_driver.ko | grep vermagic- 使用正确的内核重新编译
bash
# 如果目标板使用mainline内核
./scripts/driver_helper/build_driver.sh example-driver --kernel=mainline
# 如果目标板使用imx内核
./scripts/driver_helper/build_driver.sh example-driver --kernel=imx- 确保内核配置一致
bash
# 检查目标板的内核配置
zcat /proc/config.gz > /tmp/target.config
# 或
cat /boot/config-$(uname -r) > /tmp/target.config
# 与编译用的配置比较
diff out/mainline/linux/.config /tmp/target.config预防措施:
- ✅ 文档中明确说明目标板使用的内核类型
- ✅ 在驱动产物中包含内核版本信息
- ✅ 使用版本化的输出目录
错误类型2:缺少符号依赖
错误现象:
bash
insmod: ERROR: could not insert module fake_driver.ko: Unknown symbol
# dmesg显示: Unknown symbol function_name (err -2)可能原因:
- 驱动依赖的内核符号不存在
- 内核配置缺少必要的选项
- 驱动需要的其他模块未加载
解决步骤:
- 查看缺失的符号
bash
# 查看模块依赖
modprobe --show-depends fake_driver.ko
# 查看模块需要的符号
nm fake_driver.ko | grep ' U '
# 查看内核提供的符号
cat /proc/kallsyms- 检查内核配置
bash
# 查看当前内核配置
cat out/mainline/linux/.config | grep 相关配置项
# 查看目标板内核配置
zcat /proc/config.gz | grep 相关配置项- 加载依赖模块
bash
# 查看需要哪些模块
modprobe --show-depends fake_driver.ko
# 先加载依赖模块
sudo modprobe dependency_module
# 再加载目标模块
sudo insmod fake_driver.ko- 重新配置内核
bash
# 如果缺少内核功能,需要重新配置内核
cd third_party/linux-mainline/
make O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
menuconfig
# 找到并启用需要的选项
# 保存后重新编译内核
make O=../../out/mainline/linux \
ARCH=arm \
CROSS_COMPILE=arm-none-linux-gnueabihf- \
-j$(nproc)预防措施:
- ✅ 驱动文档中说明依赖的内核功能
- ✅ 使用Kconfig标记依赖关系
- ✅ 提供最小内核配置参考
错误类型3:权限不足
错误现象:
bash
insmod: ERROR: could not insert module fake_driver.ko: Permission denied可能原因:
- 当前用户没有root权限
- CAP_SYS_MODULE权限缺失
解决步骤:
- 使用sudo
bash
sudo insmod fake_driver.ko- 检查文件权限
bash
# 确保模块文件可读
ls -l fake_driver.ko
# 应该是 -rw-r--r--
# 如果需要,修改权限
chmod 644 fake_driver.ko- 配置权限(不推荐用于生产环境)
bash
# 允许非root用户加载模块(危险!)
# 仅用于开发环境
sudo chmod 666 /dev/mem
sudo setfacl -m u:username:rw /sys/module/预防措施:
- ✅ 文档中明确说明需要root权限
- ✅ 在脚本中检查权限并给出提示
- ✅ 提供sudo配置示例
⚠️ 运行时错误
错误类型1:设备节点未创建
错误现象:
bash
# 驱动加载成功,但找不到设备节点
ls /dev/mydevice
ls: 无法访问'/dev/mydevice': 没有那个文件或目录可能原因:
- 驱动未创建设备节点
- udev规则未正确配置
- 设备注册失败
解决步骤:
- 检查驱动日志
bash
dmesg | tail -20
# 查看是否有设备注册相关的信息
# 查找 "device" "register" "major" 等关键词- 检查设备类
bash
# 查看sysfs中的设备信息
ls -la /sys/class/
ls -la /sys/devices/- 手动创建节点(用于调试)
bash
# 查看主设备号
cat /proc/devices | grep mydevice
# 手动创建节点
sudo mknod /dev/mydevice c <major> <minor>
sudo chmod 666 /dev/mydevice- 修复驱动代码
c
// 确保驱动创建了设备节点
// 示例代码
static int __init my_init(void) {
int major;
major = register_chrdev(0, "mydevice", &fops);
if (major < 0)
return major;
// 创建设备类
my_class = class_create(THIS_MODULE, "myclass");
if (IS_ERR(my_class)) {
unregister_chrdev(major, "mydevice");
return PTR_ERR(my_class);
}
// 创建设备节点
device_create(my_class, NULL, MKDEV(major, 0), NULL, "mydevice");
return 0;
}预防措施:
- ✅ 驱动代码中自动创建设备节点
- ✅ 提供udev规则文件
- ✅ 文档中说明如何创建设备节点
错误类型2:驱动崩溃
错误现象:
bash
# 系统崩溃或重启
# 或在dmesg中看到
BUG: unable to handle kernel NULL pointer dereference可能原因:
- 空指针解引用
- 内存访问错误
- 内核API使用错误
解决步骤:
- 查看崩溃信息
bash
dmesg | tail -50
# 查看Oops信息
# 记录EIP(指令指针)地址- 使用addr2line定位错误
bash
# 从模块中提取地址信息
addr2line -e fake_driver.ko 0xaddress
# 或使用objdump
objdump -d fake_driver.ko | grep -A 20 <address>- 分析代码
c
// 检查常见的崩溃原因
// 1. 空指针解引用
struct device *dev = NULL;
dev->something = value; // ← 崩溃
// 修复:添加空指针检查
if (dev) {
dev->something = value;
}
// 2. 未初始化的指针
struct my_data *data;
data->value = 123; // ← 崩溃
// 修复:先分配内存
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->value = 123;
// 3. 数组越界
int array[10];
array[15] = 123; // ← 崩溃
// 修复:检查边界
if (index < 10)
array[index] = 123;- 使用内核调试工具
bash
# 启用内核调试选项
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
# 使用KASAN检测内存错误
CONFIG_KASAN=y
# 使用ftrace追踪函数调用
echo function > /sys/kernel/debug/tracing/current_tracer
echo my_driver > /sys/kernel/debug/tracing/set_ftrace_filter
cat /sys/kernel/debug/tracing/trace预防措施:
- ✅ 编写代码时进行空指针检查
- ✅ 使用内核的内存分配函数
- ✅ 进行边界检查
- ✅ 使用静态分析工具(如sparse)检查代码
🔍 调试技巧
1. 启用详细日志
bash
# 启用内核动态调试
echo 'module fake_driver +p' > /sys/kernel/debug/dynamic_debug/control
# 或在代码中添加
#define DEBUG 1
#include <linux/kernel.h>
// 使用pr_debug输出调试信息
pr_debug("Debug info: value=%d\n", value);2. 使用ftrace追踪
bash
# 查看可用的追踪函数
cat /sys/kernel/debug/tracing/available_filter_functions | grep fake
# 启用函数追踪
echo function > /sys/kernel/debug/tracing/current_tracer
echo fake_driver > /sys/kernel/debug/tracing/set_ftrace_filter
# 查看追踪结果
cat /sys/kernel/debug/tracing/trace
# 停止追踪
echo nop > /sys/kernel/debug/tracing/current_tracer3. 使用kprobe动态插桩
bash
# 插桩到函数
echo 'p:myprobe fake_driver_init' > /sys/kernel/debug/tracing/kprobe_events
# 启用追踪
echo 1 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable
# 查看结果
cat /sys/kernel/debug/tracing/trace
# 清理
echo 0 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable
echo '-:myprobe' >> /sys/kernel/debug/tracing/kprobe_events4. 内存泄漏检测
bash
# 启用kmemleak
echo scan > /sys/kernel/debug/kmemleak
# 查看泄漏
cat /sys/kernel/debug/kmemleak
# 清除标记
echo clear > /sys/kernel/debug/kmemleak5. 使用crash工具分析崩溃
bash
# 安装crash工具
sudo apt-get install crash
# 分析vmcore(如果系统生成了)
sudo crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux \
/var/crash/vmcore
# 常用命令
bt # 回溯
ps # 进程状态
mod -l # 列出模块
dis -l func # 反汇编函数📊 日志分析方法
1. 内核日志(dmesg)
bash
# 查看完整日志
dmesg
# 查看最近的日志
dmesg | tail -50
# 搜索特定内容
dmesg | grep -i error
dmesg | grep -i fake_driver
# 实时监控
dmesg -w
# 清除日志(慎用)
sudo dmesg -c2. 系统日志
bash
# 查看系统日志
sudo journalctl
# 查看内核日志
sudo journalctl -k
# 查看最近的日志
sudo journalctl -n 50
# 实时监控
sudo journalctl -f
# 搜索特定内容
sudo journalctl | grep -i driver3. 模块日志
bash
# 查看模块加载日志
sudo journalctl -k | grep -i module
sudo journalctl -k | grep -i insmod
# 查看特定模块的日志
sudo journalctl -k | grep fake_driver4. 用户空间日志
bash
# 如果驱动使用netlink向用户空间发送日志
# 需要编写用户空间程序接收📞 获取帮助
问题报告模板
提问或报告问题时,请提供以下信息:
markdown
## 环境信息
- 发行版本:Ubuntu 20.04
- 内核版本:5.4.0
- 工具链:arm-none-linux-gnueabihf-gcc 9.3.0
- IMX-Forge版本:[从git describe获取]
## 问题描述
[简要描述问题]
## 复现步骤
1. 执行命令:...
2. 观察到:...
3. 期望:...
## 错误信息[粘贴完整的错误输出]
## 日志输出[粘贴相关日志,如dmesg输出]
## 已尝试的解决方法
[列出已经尝试的方法和结果]
## 其他信息
[任何其他可能相关的信息]获取帮助的途径
查看文档
搜索已有问题
- GitHub Issues
- 项目FAQ
提问渠道
- GitHub Issues(推荐)
- 邮件列表
- 即时通讯群组
快速诊断检查清单
遇到问题时,按以下顺序检查:
- [ ] 内核是否已正确配置?
- [ ] 工具链是否正确安装?
- [ ] 驱动代码是否有语法错误?
- [ ] 设备树语法是否正确?
- [ ] 内核版本是否匹配?
- [ ] 是否有root权限?
- [ ] 日志中有错误信息吗?
- [ ] 其他模块是否工作正常?
相关文档:
- example_driver验证 - 基础验证流程
- 最佳实践 - 避免常见问题
- 构建脚本详解 - 深入了解构建过程