设备驱动程序与内核模块交互基本流程
- 初始化阶段:设备驱动程序作为内核模块被加载时,首先进行自身初始化。这包括分配必要的数据结构、初始化硬件相关的寄存器等操作。例如,网卡驱动需要初始化网卡的物理地址、中断号等参数。
- 注册阶段:设备驱动向内核注册自身,内核模块提供了相应的注册机制。比如,字符设备驱动通过调用
register_chrdev
函数向内核注册字符设备,将设备的主设备号、次设备号、设备操作函数等信息告知内核。
- 内核模块响应:内核模块接收到设备驱动的注册请求后,会在内核数据结构中记录该设备驱动的相关信息,如在设备驱动链表中添加该驱动节点,以便后续管理和使用。
- 运行阶段:当设备有数据传输、中断等事件发生时,设备驱动根据注册的操作函数执行相应动作,内核模块则依据驱动提供的接口与驱动进行交互,如处理设备的中断请求,将设备数据传递给上层应用等。
- 卸载阶段:当设备不再使用,设备驱动需要从内核中卸载。此时,驱动调用相应的卸载函数,如字符设备驱动调用
unregister_chrdev
函数,内核模块则从其管理的数据结构中移除该设备驱动的相关信息。
设备驱动向内核模块注册自身方式
- 字符设备驱动:通过
register_chrdev
函数注册,函数原型为 int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
,其中 major
为主设备号,name
为设备名,fops
是设备操作函数集合,定义了诸如 open
、read
、write
等对设备的操作。例如:
static struct file_operations my_fops = {
.owner = THIS_MODULE,
.read = my_read,
.write = my_write,
};
int ret = register_chrdev(0, "my_device", &my_fops);
- 块设备驱动:使用
register_blkdev
函数注册,函数原型为 int register_blkdev(unsigned int major, const char *name)
,major
为主设备号,name
为设备名。注册成功后,还需进一步注册块设备的操作函数等。
- 设备模型相关注册:通过
device_register
函数注册设备,driver_register
函数注册驱动。例如:
struct device my_device;
// 初始化my_device
device_register(&my_device);
struct device_driver my_driver;
// 初始化my_driver
driver_register(&my_driver);
内核模块为设备驱动提供的主要资源或接口
- 内存管理接口:内核提供了
kmalloc
、vmalloc
等函数,用于为设备驱动分配内核空间内存。kmalloc
分配的内存位于低端内存,物理地址连续,适合分配较小内存;vmalloc
分配的内存虚拟地址连续,物理地址不一定连续,适合分配较大内存。例如:
char *buf = kmalloc(1024, GFP_KERNEL);
if (!buf) {
// 内存分配失败处理
}
- 中断处理接口:内核提供了
request_irq
函数用于申请中断,free_irq
函数用于释放中断。设备驱动在初始化时通过 request_irq
函数注册中断处理函数,当设备产生中断时,内核调用注册的中断处理函数。例如:
int ret = request_irq(irq_num, my_irq_handler, IRQF_TRIGGER_RISING, "my_device", NULL);
if (ret) {
// 中断申请失败处理
}
- 设备管理接口:如
device_register
、driver_register
等函数,帮助设备驱动在设备模型中注册设备和驱动,使得内核能够统一管理设备和驱动,实现设备与驱动的匹配、热插拔等功能。
- 文件操作接口:内核通过
struct file_operations
结构体为设备驱动提供文件操作的框架,驱动需要填充该结构体中的函数指针,如 open
、read
、write
、release
等函数,以实现对设备的文件方式访问。应用层通过系统调用(如 open
、read
等)最终会调用到驱动填充的这些函数。
- 内核同步机制:为防止多线程或多处理器环境下对共享资源的竞争访问,内核提供了信号量(
struct semaphore
)、自旋锁(spinlock_t
)等同步机制。例如,驱动在访问共享设备寄存器时,可以使用自旋锁保护,防止多个 CPU 同时访问导致数据错误。
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);
// 访问共享资源
spin_unlock(&lock);