一、什么是字符设备?
字符设备(Character Device)是一类能像“一个字节一个字节”那样进行数据流式读写的设备,常见例子有串口、键盘、鼠标等。用户和程序通过文件操作(open、read、write、close 等)和字符设备交互。
例如:
int fd = open("/dev/mydevice", O_RDWR);
write(fd, buffer, size);
read(fd, buffer, size);
close(fd);2
3
4
目标:将用户的请求从用户空间传递到内核空间的字符驱动。
二、为什么需要字符设备框架?
如果没有统一的字符设备框架,每个驱动开发者都要单独处理文件操作、设备号分配、数据读写等底层细节,这不但复杂、容易出错,还容易造成各自为政、不一致的代码风格。
字符设备框架为驱动开发者提供了一个标准流程和接口,你只需要关注设备本身的实现(比如如何读写具体硬件),不用重复去关心如何让内核认识你的设备、如何和用户空间通信这些共性步骤。这样可以让驱动开发变得高效、规范,并且方便后期维护和升级。
说明
字符设备框架就是为了“统一入口、简化开发、避免重复、减少错误”,新手跟着框架的流程搭,就能更快地写出符合规范的驱动。
三、字符设备驱动基本流程
1. 注册字符设备
在内核中,字符设备都有一个 " 设备号 " (主设备号+次设备号),注册时会告诉系统这个驱动负责的设备号范围。
主要函数:
register_chrdev_region()或alloc_chrdev_region()- 返回一个
dev_t类型的设备号
2. 初始化并注册 cdev 结构体
注册后,需要把自己的驱动操作 “ 集合 ” (read、write等)和设备号绑定起来:
主要结构体:
struct cdev
主要函数:
cdev_init()cdev_add()
3. 实现 file_operations 接口
系统调用会通过 VFS(虚拟文件系统)进入内核。VFS的作用是把不同文件系统的操作统一管理,具体来说:
VFS通过file_operations结构体找到设备驱动中对应的操作实现。例如,当你读写文件时,VFS会根据这个结构体里的信息找到具体硬件的操作方法。file_operations是驱动程序的核心配置表,它记录了驱动如何处理各种操作(如读、写、打开文件等)。可以把它想象成驱动的"操作说明书",告诉操作系统每个动作该调用哪个具体函数。
这样设计的好处是让操作系统不用关心具体硬件细节,只要通过VFS和 file_operations 就能兼容不同设备的文件操作。
file_operations结构体定义了设备支持哪些操作,比如 open、read、write 等。
static struct file_operations xxx_fops = {
.owner = THIS_MODULE,
.open = xxx_open,
.release = xxx_release,
.read = xxx_read,
.write = xxx_write,
.... // 其他操作
// 可以根据需要只实现一部分
};2
3
4
5
6
7
8
9
4. 创建设备节点
让用户空间可以通过 /dev/xxx 访问这个设备。
一般使用class_create()、device_create()结合udev自动生成节点。
struct class* xxx_class = class_create(THIS_MODULE, "xxx_class");
device_create(xxx_class, NULL, devno, NULL, "xxx_dev");2
5. 卸载驱动和清理
cdev_del()unregister_chrdev_region()device_destroy()class_destroy()
四、应用程序的操作
应用程序通过 open() 打开字符设备文件时,内核会根据设备节点的主设备号,在字符设备注册表中找到对应驱动,然后通过驱动注册时提供的 file_operations 结构体,把用户请求(如 read、`write` 等)映射到实际的驱动处理函数,实现了用户操作与设备硬件的高效连接。