内核驱动 字符设备驱动
如果你希望写一篇更纯粹、更侧重于**“字符设备驱动基础架构”**的博文,可以参考下面这一篇。这篇博文去掉了特定的 ioctl 细节,专注于字符设备驱动的“三大支柱”:设备号管理、文件操作接口、以及内核/用户空间的数据交换。
Linux 内核开发:手把手教你构建一个规范的字符设备驱动
1. 什么是字符设备驱动?
字符设备(Character Device)是 Linux 驱动中最常见的类型之一。它像一个字节流(如文件)一样被访问。不同于块设备(如硬盘)需要随机读写,字符设备通常按照先后顺序进行数据的读取和写入,常见的键盘、串口、显示器驱动都属于这一类。
2. 字符设备驱动的“三大支柱”
开发一个字符设备驱动,主要围绕以下三个核心环节:
① 设备的“身份证明”:设备号(dev_t)
在 Linux 中,每个设备都有一个主设备号(Major Number)和次设备号(Minor Number)。
- 主设备号:对应驱动程序。
- 次设备号:对应驱动程序控制的具体设备。
规范写法:建议使用alloc_chrdev_region让内核自动分配,避免手动指定的冲突。
② 驱动的“大脑”:文件操作接口(file_operations)
这是用户态与内核态的“翻译官”。当用户调用 read() 或 write() 时,内核会通过这个结构体找到驱动中对应的函数。
③ 安全的“关卡”:空间拷贝
内核空间和用户空间是隔离的。驱动程序绝对不能直接解引用用户态传来的指针,必须使用:
copy_from_user():安全地从用户态获取数据。copy_to_user():安全地发送数据给用户态。
3. 核心代码框架解析
我们将实现一个简单的“内存回显设备”,用户写入什么,下次就能读取出什么。
3.1 驱动初始化:注册与自动创建节点
现代驱动不再需要手动执行 mknod 命令,通过 class_create 和 device_create 可以实现 /dev/ 下节点的自动生成。
1 | static int __init mychardev_init(void) { |
3.2 读写逻辑:内存数据交换
为了保证多进程访问时的安全,我们引入了 mutex 互斥锁。
1 | static ssize_t my_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { |
4. 实验与验证
编译驱动
使用标准的内核 Makefile 编译生成 .ko 文件:
1 | make |
快速测试
甚至不需要写 C 程序,直接利用 Shell 命令即可测试驱动:
1 | # 写入测试 |
查看内核日志
1 | dmesg | tail |
你会看到驱动输出的加载信息以及读写字节数。
5. 学习总结与进阶建议
编写字符设备驱动时,有几个规范必须遵守:
- 始终检查返回值:内核是脆弱的,任何分配失败(如内存、设备号)都必须有完善的
goto回滚逻辑。 - 保护共享资源:只要有全局变量,就必须考虑并发。互斥锁(Mutex)是最简单有效的保护方式。
- 遵循 GPL 协议:在模块末尾声明
MODULE_LICENSE("GPL")。
下一步建议:在掌握了基础读写后,可以尝试研究 poll(非阻塞IO)或 unlocked_ioctl(设备控制),这些是通往中级驱动工程师的必经之路。
本文是原创文章,采用CC BY-NC-SA 4.0协议,完整转载请注明来自CtaNgH
评论




