Skip to content

错误排查指南

目标读者:所有用户

难度级别:🟡 中级

包含内容:常见错误、解决方案、调试技巧、日志分析

📋 目录

🔧 编译错误

错误类型1:内核未配置

错误现象

bash
========================================
 内核未正确编译
========================================
内核类型: 主线内核 (linux_mainline)
内核目录: /path/to/linux_mainline
输出目录: /path/to/out/mainline/linux

缺少以下文件:
  - 内核配置文件: .config
  - autoconf.h
  - Module.symvers (需要运行 modules_prepare)

可能原因

  • 首次使用,内核源码未配置
  • 内核输出目录被清理
  • 切换了内核类型但未重新配置

解决步骤

  1. 检查内核源码是否存在
bash
ls -la third_party/linux-mainline/
# 应该看到很多文件和目录
  1. 配置内核
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
  1. 准备内核模块
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环境变量
  • 工具链安装路径不正确

解决步骤

  1. 检查工具链是否存在
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
  1. 安装工具链

Ubuntu/Debian:

bash
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf

Fedora/RHEL:

bash
sudo dnf install arm-linux-gnueabihf-gcc
  1. 添加到PATH
bash
# 临时添加(当前会话有效)
export PATH=/path/to/toolchain/bin:$PATH

# 永久添加(添加到~/.bashrc或~/.zshrc)
echo 'export PATH=/path/to/toolchain/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
  1. 验证安装
bash
arm-none-linux-gnueabihf-gcc --version
# 应该显示版本信息

预防措施

  • ✅ 使用环境管理脚本自动配置工具链路径
  • ✅ 在项目的环境初始化脚本中设置PATH
  • ✅ 文档中明确说明工具链要求

错误类型3:驱动源码语法错误

错误现象

bash
fake_driver.c:25:2: error: expected ';' before 'return'
   return 0;
   ^

可能原因

  • C代码语法错误
  • 缺少必要的头文件
  • 内核API使用错误

解决步骤

  1. 查看完整错误信息
bash
# 重新编译,查看完整错误
./scripts/driver_helper/build_driver.sh example-driver 2>&1 | tee build.log
  1. 定位错误行
bash
# 查看错误行附近的代码
sed -n '20,30p' driver/example-driver/alpha-board/fake_driver.c
  1. 修正错误
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;
}
  1. 使用代码检查工具
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

可能原因

  • 设备树语法错误
  • 节点名称格式错误
  • 引用的节点不存在

解决步骤

  1. 手动编译查看详细错误
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
  1. 常见语法错误和修正
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";  // ← 添加分号
};
  1. 使用设备树编译器检查
bash
# 只检查语法,不生成文件
dtc -I dts -O dtb -o /dev/null imx6ull-aes-example-driver.dts

预防措施

  • ✅ 使用支持设备树语法高亮的编辑器
  • ✅ 参考内核文档中的设备树规范
  • ✅ 编译前先进行语法检查

🚚 部署错误

错误类型1:目标目录不存在

错误现象

bash
[ERROR] 目录不存在: /srv/tftp

可能原因

  • TFTP服务器未配置
  • NFS目录未创建
  • 配置文件中的路径不正确

解决步骤

  1. 检查配置文件
bash
cat scripts/driver_helper/driver_helper.conf

# 查看路径配置
# TFTP_DIR="${HOME}/tftp"
# NFS_DIR="rootfs/nfs"
  1. 创建目标目录
bash
# 创建TFTP目录
mkdir -p ~/tftp

# 创建NFS目录
mkdir -p rootfs/nfs
  1. 更新配置文件
bash
# 编辑配置文件
vim scripts/driver_helper/driver_helper.conf

# 修改路径
TFTP_DIR="/你的实际/tftp/路径"
NFS_DIR="/你的实际/nfs/路径"
  1. 配置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权限
  • 当前用户不在正确的组中
  • 文件系统权限设置不当

解决步骤

  1. 使用sudo部署
bash
# 方法1:使用sudo
sudo ./scripts/driver_helper/deploy_driver.sh \
  out/driver_artifacts/example-driver/alpha-board \
  --target=tftp
  1. 修改目录权限
bash
# 修改目录所有者
sudo chown -R $USER:$USER ~/tftp
sudo chown -R $USER:$USER rootfs/nfs

# 或者添加写权限
chmod o+w /srv/tftp
  1. 将用户添加到正确的组
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服务未运行
  • 网络连接问题
  • 主机名或端口配置错误

解决步骤

  1. 测试网络连接
bash
# 测试主机是否可达
ping remote-host

# 测试SSH端口
telnet remote-host 22
nc -zv remote-host 22
  1. 检查SSH配置
bash
# 检查本地SSH配置
cat ~/.ssh/config

# 测试SSH连接
ssh -v user@remote-host
  1. 配置SSH密钥认证
bash
# 生成SSH密钥(如果还没有)
ssh-keygen -t rsa -b 4096

# 复制公钥到远程主机
ssh-copy-id user@remote-host

# 测试免密登录
ssh user@remote-host
  1. 检查远程主机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'

可能原因

  • 驱动编译时使用的内核与运行时内核不同
  • 内核配置不匹配

解决步骤

  1. 检查内核版本
bash
# 在目标板上检查运行时内核版本
uname -r

# 在开发机上检查驱动编译的内核版本
modinfo fake_driver.ko | grep vermagic
  1. 使用正确的内核重新编译
bash
# 如果目标板使用mainline内核
./scripts/driver_helper/build_driver.sh example-driver --kernel=mainline

# 如果目标板使用imx内核
./scripts/driver_helper/build_driver.sh example-driver --kernel=imx
  1. 确保内核配置一致
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)

可能原因

  • 驱动依赖的内核符号不存在
  • 内核配置缺少必要的选项
  • 驱动需要的其他模块未加载

解决步骤

  1. 查看缺失的符号
bash
# 查看模块依赖
modprobe --show-depends fake_driver.ko

# 查看模块需要的符号
nm fake_driver.ko | grep ' U '

# 查看内核提供的符号
cat /proc/kallsyms
  1. 检查内核配置
bash
# 查看当前内核配置
cat out/mainline/linux/.config | grep 相关配置项

# 查看目标板内核配置
zcat /proc/config.gz | grep 相关配置项
  1. 加载依赖模块
bash
# 查看需要哪些模块
modprobe --show-depends fake_driver.ko

# 先加载依赖模块
sudo modprobe dependency_module

# 再加载目标模块
sudo insmod fake_driver.ko
  1. 重新配置内核
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权限缺失

解决步骤

  1. 使用sudo
bash
sudo insmod fake_driver.ko
  1. 检查文件权限
bash
# 确保模块文件可读
ls -l fake_driver.ko
# 应该是 -rw-r--r--

# 如果需要,修改权限
chmod 644 fake_driver.ko
  1. 配置权限(不推荐用于生产环境)
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规则未正确配置
  • 设备注册失败

解决步骤

  1. 检查驱动日志
bash
dmesg | tail -20

# 查看是否有设备注册相关的信息
# 查找 "device" "register" "major" 等关键词
  1. 检查设备类
bash
# 查看sysfs中的设备信息
ls -la /sys/class/
ls -la /sys/devices/
  1. 手动创建节点(用于调试)
bash
# 查看主设备号
cat /proc/devices | grep mydevice

# 手动创建节点
sudo mknod /dev/mydevice c <major> <minor>
sudo chmod 666 /dev/mydevice
  1. 修复驱动代码
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使用错误

解决步骤

  1. 查看崩溃信息
bash
dmesg | tail -50

# 查看Oops信息
# 记录EIP(指令指针)地址
  1. 使用addr2line定位错误
bash
# 从模块中提取地址信息
addr2line -e fake_driver.ko 0xaddress

# 或使用objdump
objdump -d fake_driver.ko | grep -A 20 <address>
  1. 分析代码
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;
  1. 使用内核调试工具
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_tracer

3. 使用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_events

4. 内存泄漏检测

bash
# 启用kmemleak
echo scan > /sys/kernel/debug/kmemleak

# 查看泄漏
cat /sys/kernel/debug/kmemleak

# 清除标记
echo clear > /sys/kernel/debug/kmemleak

5. 使用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 -c

2. 系统日志

bash
# 查看系统日志
sudo journalctl

# 查看内核日志
sudo journalctl -k

# 查看最近的日志
sudo journalctl -n 50

# 实时监控
sudo journalctl -f

# 搜索特定内容
sudo journalctl | grep -i driver

3. 模块日志

bash
# 查看模块加载日志
sudo journalctl -k | grep -i module
sudo journalctl -k | grep -i insmod

# 查看特定模块的日志
sudo journalctl -k | grep fake_driver

4. 用户空间日志

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输出]


## 已尝试的解决方法
[列出已经尝试的方法和结果]

## 其他信息
[任何其他可能相关的信息]

获取帮助的途径

  1. 查看文档

  2. 搜索已有问题

    • GitHub Issues
    • 项目FAQ
  3. 提问渠道

    • GitHub Issues(推荐)
    • 邮件列表
    • 即时通讯群组

快速诊断检查清单

遇到问题时,按以下顺序检查:

  • [ ] 内核是否已正确配置?
  • [ ] 工具链是否正确安装?
  • [ ] 驱动代码是否有语法错误?
  • [ ] 设备树语法是否正确?
  • [ ] 内核版本是否匹配?
  • [ ] 是否有root权限?
  • [ ] 日志中有错误信息吗?
  • [ ] 其他模块是否工作正常?

相关文档

Built with VitePress