MST
星途 面试题库

面试题:设备管理之设备驱动与内核模块交互基础

简述设备驱动程序与内核模块交互的基本流程,并且说明在这个过程中,设备驱动如何向内核模块注册自身,以及内核模块为设备驱动提供了哪些主要的资源或接口?
16.8万 热度难度
操作系统设备管理

知识考点

AI 面试

面试题答案

一键面试

设备驱动程序与内核模块交互基本流程

  1. 初始化阶段:设备驱动程序作为内核模块被加载时,首先进行自身初始化。这包括分配必要的数据结构、初始化硬件相关的寄存器等操作。例如,网卡驱动需要初始化网卡的物理地址、中断号等参数。
  2. 注册阶段:设备驱动向内核注册自身,内核模块提供了相应的注册机制。比如,字符设备驱动通过调用 register_chrdev 函数向内核注册字符设备,将设备的主设备号、次设备号、设备操作函数等信息告知内核。
  3. 内核模块响应:内核模块接收到设备驱动的注册请求后,会在内核数据结构中记录该设备驱动的相关信息,如在设备驱动链表中添加该驱动节点,以便后续管理和使用。
  4. 运行阶段:当设备有数据传输、中断等事件发生时,设备驱动根据注册的操作函数执行相应动作,内核模块则依据驱动提供的接口与驱动进行交互,如处理设备的中断请求,将设备数据传递给上层应用等。
  5. 卸载阶段:当设备不再使用,设备驱动需要从内核中卸载。此时,驱动调用相应的卸载函数,如字符设备驱动调用 unregister_chrdev 函数,内核模块则从其管理的数据结构中移除该设备驱动的相关信息。

设备驱动向内核模块注册自身方式

  1. 字符设备驱动:通过 register_chrdev 函数注册,函数原型为 int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops),其中 major 为主设备号,name 为设备名,fops 是设备操作函数集合,定义了诸如 openreadwrite 等对设备的操作。例如:
static struct file_operations my_fops = {
   .owner = THIS_MODULE,
   .read = my_read,
   .write = my_write,
};
int ret = register_chrdev(0, "my_device", &my_fops);
  1. 块设备驱动:使用 register_blkdev 函数注册,函数原型为 int register_blkdev(unsigned int major, const char *name)major 为主设备号,name 为设备名。注册成功后,还需进一步注册块设备的操作函数等。
  2. 设备模型相关注册:通过 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);

内核模块为设备驱动提供的主要资源或接口

  1. 内存管理接口:内核提供了 kmallocvmalloc 等函数,用于为设备驱动分配内核空间内存。kmalloc 分配的内存位于低端内存,物理地址连续,适合分配较小内存;vmalloc 分配的内存虚拟地址连续,物理地址不一定连续,适合分配较大内存。例如:
char *buf = kmalloc(1024, GFP_KERNEL);
if (!buf) {
    // 内存分配失败处理
}
  1. 中断处理接口:内核提供了 request_irq 函数用于申请中断,free_irq 函数用于释放中断。设备驱动在初始化时通过 request_irq 函数注册中断处理函数,当设备产生中断时,内核调用注册的中断处理函数。例如:
int ret = request_irq(irq_num, my_irq_handler, IRQF_TRIGGER_RISING, "my_device", NULL);
if (ret) {
    // 中断申请失败处理
}
  1. 设备管理接口:如 device_registerdriver_register 等函数,帮助设备驱动在设备模型中注册设备和驱动,使得内核能够统一管理设备和驱动,实现设备与驱动的匹配、热插拔等功能。
  2. 文件操作接口:内核通过 struct file_operations 结构体为设备驱动提供文件操作的框架,驱动需要填充该结构体中的函数指针,如 openreadwriterelease 等函数,以实现对设备的文件方式访问。应用层通过系统调用(如 openread 等)最终会调用到驱动填充的这些函数。
  3. 内核同步机制:为防止多线程或多处理器环境下对共享资源的竞争访问,内核提供了信号量(struct semaphore)、自旋锁(spinlock_t)等同步机制。例如,驱动在访问共享设备寄存器时,可以使用自旋锁保护,防止多个 CPU 同时访问导致数据错误。
spinlock_t lock;
spin_lock_init(&lock);
spin_lock(&lock);
// 访问共享资源
spin_unlock(&lock);