第二十章 字符设备驱动开发
1



问题原因
- 缺少 C/C++ 扩展(最常见):没有安装微软官方
C/C++扩展,命令不会出现 - 文件夹处于【受限模式】(截图顶部明显提示):未信任文件夹,部分命令被隐藏
- 输入命令不全、大小写问题:完整命令是
C/C++: Edit Configurations (JSON) - VSCode 版本过旧
✅ 分步解决
第 1 步:信任文件夹(先处理截图顶部警告)
点击顶部蓝色文字:管理 → 信任此文件夹
受限模式下,C/C++ 的配置命令会直接隐藏,这是你当前首要问题!
第 2 步:安装 C/C++ 官方扩展
左侧扩展栏(四个方块图标),搜索: C/C++ 作者:Microsoft(ms-vscode.cpptools),点击安装,安装完成后重启 VSCode。

modprobe chrdevbase 报错 disagrees about version of symbol module_layout
报错 disagrees about version of symbol module_layout = 编译 ko 所用内核源码 ≠ 开发板当前运行内核的原始编译源码(虽然版本号同为 5.4.31,但是 .config、Module.symvers、CRC 校验不一致)

重中之重:.config 配置文件必须和板子固件原始编译配置一模一样
板子固件编译时间:Thu Jan 12 15:16:06 CST 2023 你现在用的 imx_v7_defconfig 不一定等于固件当年使用的配置
- 正点原子固件包内一般附带:固件配套
.config,复制到内核源码根目录覆盖 - 任意配置差异(SMP、PREEMPT、MODVERSIONS)都会直接导致 vermagic / CRC 不匹配
modprobe chrdevbase.ko 与 modprobe chrdevbase 的区别
这两个命令在 Linux 内核模块加载中有着本质区别,理解它们对于解决模块加载失败至关重要。
modprobe chrdevbase.ko:这是一个错误的用法。modprobe命令设计用于加载位于标准模块目录(如/lib/modules/$(uname -r)/)中的模块,它通过模块名(而不是带.ko后缀的文件名)在模块依赖关系数据库(modules.dep)中查找并加载模块及其依赖。直接指定.ko文件路径不是modprobe的标准用法,通常会导致命令失败或行为不符合预期。modprobe chrdevbase:这是正确的标准用法。它指示系统加载名为chrdevbase的模块。系统会:- 在
/lib/modules/$(uname -r)/modules.dep文件中查找chrdevbase.ko的实际路径。 - 检查并加载
chrdevbase所依赖的其他模块(如果有)。 - 执行模块的初始化函数,将模块插入到运行中的内核。
make modules_install正确安装到了标准模块目录,并且生成了正确的依赖文件。- 在
对于驱动开发调试的实际情况:在开发过程中,我们通常在源码目录下编译出 .ko 文件,但并未将其安装到系统标准目录。此时应该使用 insmod chrdevbase.ko 命令。insmod 是“插入模块”的意思,它不处理模块依赖,直接加载指定的 .ko 文件。这是开发阶段加载自定义驱动模块最常用、最直接的方法。
总结与选择:
- 开发/测试阶段:在编译目录下,使用
insmod chrdevbase.ko。 - 生产/部署阶段:将模块安装到系统后,使用
modprobe chrdevbase。 - 避免使用:
modprobe chrdevbase.ko(非标准用法)。
因此,如果你在开发板尝试加载自己编译的 chrdevbase.ko 时遇到 disagrees about version of symbol module_layout 错误,根本原因在于内核版本不匹配(如前文所述),与使用 modprobe 还是 insmod 无关。但使用正确的命令(insmod)可以避免因命令使用不当而产生的额外困惑。
>.config 覆盖源码根目录
- 完整编译内核:
make xxx modules -j4(生成 Module.symvers) - 编译驱动模块(Makefile 指向这套配套源码)
- 拷贝新 ko 到开发板,
modprobe正常加载
📎 Makefile 标准模板(确认你当前 Makefile 是否完整)
KERNELDIR := /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31
CURRENT_PATH := $(shell pwd)
obj-m := chrdevbase.o
build:
make -C $(KERNELDIR) M=$(CURRENT_PATH) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
clean:
make -C $(KERNELDIR) M=$(CURRENT_PATH) clean
- 虚拟机:
strings chrdevbase.ko | grep vermagic - 开发板:
cat /proc/version
虚拟机 strings chrdevbase.ko | grep vermagic

开发板 cat /proc/version

最终重新5.4.31 编译 将tm32mp157d-atk.dtb uImage 挂在 tftp


modprobe chrdevbase.ko modprobe chrdevbase 什么区别


modprobe chrdevbase vs modprobe chrdevbase.ko
标准规范:modprobe 后面跟【模块名】,不带 .ko(
chrdevbase) 带.ko(chrdevbase.ko)属于非常规写法,行为不一样
1. modprobe chrdevbase(标准写法,教程推荐)
- 去
modules.dep(depmod 生成的依赖表)查找 chrdevbase 这个模块 - 读取依赖,自动加载它依赖的其他内核模块
- 加载成功后,
lsmod里面模块名称就是chrdevbase - 卸载配套:
modprobe -r chrdevbase(同样不带后缀)
2. modprobe chrdevbase.ko(带后缀,不规范)
- modprobe 会把 chrdevbase.ko 整体当成模块名 去 modules.dep 查找
- 如果 modules.dep 里没有名为
chrdevbase.ko的条目,大概率直接报错:module xxx.ko not found in modules.dep - 部分新版 kmod 兼容处理:识别成文件名,直接加载 ko,不再自动处理完整依赖,行为接近 insmod
你这次碰巧加载成功属于兼容特例,不具备通用性,换环境很容易报错
📌 结合你截图里现象解释
modprobe chrdevbase.ko
[ 450.473140] chrdevbase init! # 加载成功(兼容模式)
modprobe chrdevbase
# 无打印,因为模块已经加载过了!
lsmod 能看到 chrdevbase
第二次执行 modprobe chrdevbase 没有打印 chrdevbase init! 👉 模块一旦加载成功,再次 modprobe 不会重复执行入口函数,不会再次打印 init 卸载后(rmmod chrdevbase)再 modprobe 才会重新打印 init
🆚 补充区分三条命令
modprobe chrdevbase✅ 标准:查 modules.dep,自动加载依赖modprobe chrdevbase.ko❌ 非标准:尝试按文件名加载,依赖处理失效,极易报错insmod chrdevbase.ko:直接指定 ko 文件加载,完全不解析依赖
⚠️ 额外提示你截图里的警告
module verification failed: signature and/or required key missing - tainting kernel
含义:你的内核开启模块签名校验,但 ko 没有签名;驱动依然可以加载,只是内核标记为 tainted(被污染),不影响实验学习,正式产品需要处理签名。
✅ 开发实验推荐固定用法
# 加载
modprobe chrdevbase
# 卸载
modprobe -r chrdevbase
输入“lsmod”命令即可查看当前系统中存在的模块
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe chrdevbase
[ 1583.815287] chrdevbase init!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module Size Used by Tainted: G
chrdevbase 16384 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
当前系统只有“chrdevbase”这一个模块。输入如下命令查看当前 系统中有没有 chrdevbase 这个设备: cat /proc/devices

当前系统存在 chrdevbase 这个设备,主设备号为 200,跟我们设置的主设备号一致。

创建设备节点文件 驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操 作这个设备节点文件来完成对具体设备的操作。输入如下命令创建/dev/chrdevbase 这个设备节 点文件:
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ mknod /dev/chrdevbase c 200 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
其中“mknod”是创建节点命令,“/dev/chrdevbase”是要创建的节点文件,“c”表示这是个 字符设备,“200”是设备的主设备号,“0”是设备的次设备号。创建完成以后就会存在 /dev/chrdevbase 这个文件,可以使用“ls /dev/chrdevbase -l”命令查看
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls /dev/chrdevbase -l
crw-r--r-- 1 root root 200, 0 Feb 21 17:20 /dev/chrdevbase
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
如果 chrdevbaseAPP 想要读写 chrdevbase 设备,直接对/dev/chrdevbase 进行读写操作即可。 相当于/dev/chrdevbase 这个文件是 chrdevbase 设备在用户空间中的实现。前面一直说 Linux 下 一切皆文件,包括设备也是文件。
chrdevbase 设备操作测试
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./chrdevbaseApp /dev/chrdevbase 1
[ 2231.625077] kernel senddata ok!
read data:kernel data!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./chrdevbaseApp /dev/chrdevbase 2
[ 2265.073573] kernel recevdata:usr data!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
chrdevbase_read 函数输出的信息,因为 chrdevbaseAPP 使用 read 函数从 chrdevbase 设备读取数 据,因此 chrdevbase_read 函数就会执行。chrdevbase_read 函数向 chrdevbaseAPP 发送“kernel data!”数据,chrdevbaseAPP 接收到以后就打印出来,“read data:kernel data!”就是 chrdevbaseAPP 打印出来的接收到的数据。说明对 chrdevbase 的读操作正常,接下来测试对 chrdevbase 设备的 写操作,输入如下命令: ./chrdevbaseApp /dev/chrdevbase 2
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./chrdevbaseApp /dev/chrdevbase 2
[ 2265.073573] kernel recevdata:usr data!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
只有一行“kernel recevdata:usr data!”,这个是驱动程序中的 chrdevbase_write 函数输出的。 chrdevbaseAPP 使用 write 函数向 chrdevbase 设备写入数据“usr data!”。chrdevbase_write 函数接 收到以后将其打印出来。说明对 chrdevbase 的写操作正常,既然读写都没问题,说明我们编写 的 chrdevbase 驱动是没有问题的
如果不再使用某个设备的话可以将其驱动卸载掉,比如输入如下命令卸载掉 chrdevbase 这 个设备: rmmod chrdevbase
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs$ cd 5.4.31/
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs/5.4.31$ ls
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs/5.4.31$ rmmod chrdevbase
[ 5094.423605] chrdevbase exit!
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs/5.4.31$ lsmod
Module Size Used by Tainted: G
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs/5.4.31$
至此,chrdevbase 这个设备的整个驱动就验证完成了,驱动工作正常。本章我们详细的讲 解了字符设备驱动的开发步骤,并且以一个虚拟的 chrdevbase 设备为例,带领大家完成了第一 个字符设备驱动的开发,掌握了字符设备驱动的开发框架以及测试方法
rm: 无法删除'/home/user/linux/Drivers/Linux_Drivers/01chrdebbase/Module.symvers': 权限不够

报错原因
rm: 无法删除 'Module.symvers': 权限不够
Module.symvers 文件之前大概率是用 sudo/root 权限生成的,现在普通 user 用户执行 make clean,没有删除权限。
✅ 方案 1(推荐,手动清理)
进入驱动目录执行:
sudo rm -f *.o *.ko *.mod.c Module.symvers modules.order .*.cmd
make clean
✅ 方案 2 直接 sudo 执行 clean
sudo make clean
⚠️ 注意:不建议长期用 sudo 编译驱动,容易后续所有文件权限全部变成 root,反复权限报错;清理完之后编译最好还是普通 user 用户:
make
✅ 方案 3 修复整个文件夹所有者(根治权限问题)
把驱动目录全部改成当前 user 拥有,以后不会再报权限不足
sudo chown -R user:user /home/user/linux/Drivers/Linux_Drivers/01chrdebbase
make clean
📝 顺带检查你的 Makefile 小优化(不报错,写法可以简化,适配原子教程)
你现在的 Makefile 可以精简成标准模板,功能一致:
KERNELDIR ?= /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31
PWD := $(shell pwd)
obj-m += chrdevbase.o
all:
make -C $(KERNELDIR) M=$(PWD) modules
clean:
make -C $(KERNELDIR) M=$(PWD) clean
❗ 补充提醒
- 编译驱动
make不要加 sudo!一旦 sudo make,生成文件归属 root,下次 clean 必报权限不足 - 确认:
linux-5.4.31内核源码已经提前完整编译过make modules,否则 ko 依然会版本不匹配
清理完成后直接:
make
生成新的 chrdevbase.ko 传到开发板测试即可!
如果你需要,我还可以告诉你:如何查看文件归属 ls -l Module.symvers 确认是不是 root 权限。

问题分析
- 权限问题:
ls -l看到所有文件所有者都是root root,普通user用户无法正常删除编译产物,make clean会权限报错。 - 编译报错:多个目标匹配:是 Makefile 写法问题 + 旧编译文件残留共同引发。
目录里遗留了root 创建的隐藏依赖文件 .chrdevbase.o.d,普通 user 用户无法覆盖写入编译临时文件。
make -C /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31 M=/home/user/linux/Drivers/Linux_Drivers/01chrdebbase modules
make[1]: 进入目录“/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31”
CC [M] /home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.o
/home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.c:147:2: 致命错误: 打开依赖文件 /home/user/linux/Drivers/Linux_Drivers/01chrdebbase/.chrdevbase.o.d:权限不够
147 | MODULE_INFO(intree, "Y");
| ^~~~~~~~~~~
编译中断。
scripts/Makefile.build:265: recipe for target '/home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.o' failed
make[2]: *** [/home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.o] Error 1
Makefile:1692: recipe for target '/home/user/linux/Drivers/Linux_Drivers/01chrdebbase' failed
make[1]: *** [/home/user/linux/Drivers/Linux_Drivers/01chrdebbase] Error 2
make[1]: 离开目录“/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31”
Makefile:8: recipe for target 'kernel_modules' failed
make: *** [kernel_modules] Error 2
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$ sudo chown -R user:user /home/user/linux/Drivers/Linux_Drivers/01chrdebbase
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$ make
make -C /home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31 M=/home/user/linux/Drivers/Linux_Drivers/01chrdebbase modules
make[1]: 进入目录“/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31”
CC [M] /home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] /home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.mod.o
LD [M] /home/user/linux/Drivers/Linux_Drivers/01chrdebbase/chrdevbase.ko
make[1]: 离开目录“/home/user/linux/atk-mp1/linux/my_linux/linux-5.4.31”
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$
编译APP
arm-none-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$ arm-none-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$ la
chrdevbaseApp chrdevbase.c .chrdevbase.ko.cmd chrdevbase.mod.c chrdevbase.mod.o chrdevbase.o Makefile Module.symvers
chrdevbaseApp.c chrdevbase.ko chrdevbase.mod .chrdevbase.mod.cmd .chrdevbase.mod.o.cmd .chrdevbase.o.cmd modules.order
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$
nfs 挂载路径没有文件
showmount -e 查看nfs 共享路径
user@user-virtual-machine:~/linux/Drivers/Linux_Drivers/01chrdebbase$ cd /home/user/linux/nfs/rootfs/lib/modules/5.4.31/
user@user-virtual-machine:~/linux/nfs/rootfs/lib/modules/5.4.31$ ls
aaaa chrdevbaseApp chrdevbase.ko
user@user-virtual-machine:~/linux/nfs/rootfs/lib/modules/5.4.31$ ls
aaaa chrdevbaseApp chrdevbase.ko
user@user-virtual-machine:~/linux/nfs/rootfs/lib/modules/5.4.31$ touch bbb
user@user-virtual-machine:~/linux/nfs/rootfs/lib/modules/5.4.31$ showmount -e
Export list for user-virtual-machine:
/home/user/linux/nfs *
user@user-virtual-machine:~/linux/nfs/rootfs/lib/modules/5.4.31$
[root@ATK-stm32mp1]:/home/user/getnfs/rootfs/lib/modules/5.4.31$ cd /lib/modules
/5.4.31/
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa chrdevbaseApp modules.dep
chrdevbase.ko modules.alias modules.symbols
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rm -f *
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cp /home/user/getnfs/rootfs/lib/module
s/5.4.31/* ./
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa bbb chrdevbase.ko chrdevbaseApp
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ depmod
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ modprobe chrdevbase
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls
aaaa chrdevbase.ko modules.alias modules.symbols
bbb chrdevbaseApp modules.dep
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ lsmod
Module Size Used by Tainted: G
chrdevbase 16384 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyRPMSG
7 vcs
10 misc
13 input
21 sg
29 fb
89 i2c
90 mtd
108 ppp
116 alsa
128 ptm
136 pts
153 spi
166 ttyACM
180 usb
188 ttyUSB
189 usb_device
200 chrdevbase
216 rfcomm
226 drm
242 rpmb
243 ttyGS
244 ttyUSI
245 ttySTM
246 bsg
247 watchdog
248 tee
249 iio
250 ptp
251 pps
252 cec
253 rtc
254 gpiochip
Block devices:
1 ramdisk
7 loop
8 sd
11 sr
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 virtblk
259 blkext
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ mknod /dev/chrdevbase c 200 0
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ls /dev/chrdevbase -l
crw-r--r-- 1 root root 200, 0 Feb 21 19:18 /dev/chrdevbase
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./chrdevbaseApp /dev/chrdevbase 1
[ 8857.040525] chrdevbase init!
[ 8957.094541] chrdevbase open!
[ 8957.098927] kernel senddata ok!
read data:kernel data!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ ./chrdevbaseApp /dev/chrdevbase 2
[ 8974.459888] chrdevbase open!
[ 8974.461390] kernel recevdata:usr data!
write data:usr data!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$ rmmod chrdevbase
[ 9001.674865] chrdevbase exit!
[root@ATK-stm32mp1]:/lib/modules/5.4.31$
更多推荐



所有评论(0)