1. 为什么字符设备驱动是Linux驱动开发的基石
在嵌入式软件工程师的面试中,Linux字符设备驱动开发几乎是必考的核心知识点。我当年第一次面试嵌入式驱动岗位时,就被要求在白板上手写一个完整的字符设备驱动框架。当时虽然背了模板代码,但对背后的设计理念一知半解,结果被面试官几个"为什么"问得哑口无言。
字符设备驱动之所以重要,是因为它代表了Linux设备驱动最基础的设计哲学。与块设备和网络设备不同,字符设备以字节流形式进行数据交互,这种抽象使得它可以覆盖从简单的按键到复杂的传感器等绝大多数硬件外设。在/proc/devices中,你会看到主设备号1的mem、主设备号4的tty都是字符设备——这足以说明它在系统中的基础地位。
2. 字符设备驱动的核心数据结构剖析
2.1 file_operations结构体:驱动与VFS的契约
这个结构体定义了驱动提供给虚拟文件系统(VFS)的所有操作接口。在最新Linux内核(5.x)中,它包含超过30个函数指针,但实际开发中常用的有:
c复制static const struct file_operations mydev_fops = {
.owner = THIS_MODULE,
.open = mydev_open,
.release = mydev_release,
.read = mydev_read,
.write = mydev_write,
.unlocked_ioctl = mydev_ioctl,
.llseek = mydev_llseek,
};
特别要注意的是.owner字段必须初始化为THIS_MODULE,这是内核模块引用计数的关键。我在早期项目中曾漏掉这个设置,导致模块卸载时系统崩溃。
2.2 cdev与inode:内核的对象管理艺术
struct cdev代表内核中的字符设备对象,需要通过cdev_init()初始化并与file_operations绑定。而struct inode则包含了内核管理文件系统对象的所有元信息。这两个结构体的关系可以用生活中的快递柜类比:
inode相当于快递柜的格子编号和物理属性(大小、位置等)cdev相当于存放在格子里
