一、等待队列在linux驱动程序中,可以使用等待队列来实现阻塞进程的唤醒。等待队列是以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。等待队列可以用来同步对系统资源的访问,在内核中,信号量也是依赖于等待队列来实现的。
作用:所有的等待资源的任务都被放入到等待队列中。
二、实现等待队列是一个队列数据结构所以,队列有等待队列头和等待队列项。
1、定义“等待队列头”wait_queue_head_t my_head;在使用的时候要先初始化以下,定义了几个队列,就初始化几次。
2、初始化“等待队列头”init_waitqueue_head(&my_head);在内核中include/linux/wait.h中定义如下
struct lock_class_key 在内核中定义为空的结构体
在kernel/wait.c中__init_waitqueue_head函数的实现
第一句话的意思是自旋锁的初始化。最后一句话的意思是初始化队列头。在include/linux/list.h中有实现代码:
在内核中为了方便使用,有一个宏可以完成等待队列头的定义和初始化:
DECLARE_WAIT-QUEUE_HEAD(name); name为队列头的名字3、定义等待队列DECLARE_WAITQUEUE(name,tsk);定义并初始化一个名为name的等待队列name就是等待队列的名字,也是等待队列头的名字,tsk一般是写如当前的任务(进程)current(内核中的全局变量,代表当前任务)。
4、添加/移除等待队列void fastcall add_wait_queue(wait_queue_heat_t *q,wait_queue_t *wait);void fastcall remove_wait_queue(wait_queue_head_t *q ,wait_queue_t*wait)5、等待事件wait_event(queue,condition);不可中断的等待事件wait_event_interruptible(queue,condition);可中断的等待事件,所以在使用时候,要先判断是否由于中断返回还是有资源返回。wait_event_timeout(queue,condition,timeout);timeout是等待的时间,超时就会返回,同样的也是不可中断的wait_event_interruptible_timeout(queue,condition,timeout);同样的是可中断,使用的时候也要判断。Condition为满足的条件。
6、唤醒队列void wake_up(wait_queue_head_t *queue);唤醒等待队列中的所有的任务void wake_up_interruptible(wait_queue_head_t *queue);只唤醒可中断等待的任务,7、在等待队列上睡眠sleep_on(wait_queue_head_t *q);把当前任务设置为不可中断,不可中断的等待态,并创建一个等待队列,并加入这个新创建的等待队列interruptible_sleep_on(wait_queue_head_t *q);与上面的一样,就是,设置状态为可中断的等待态。三、程序分析在本程序中用到了简单睡眠和手动睡眠两种方法。本驱动程序中的缓冲区是一个循环的缓冲区,类似与循环队列。当rp == rw时候说明缓冲区空,定
rp与rw相差1时候,说明缓冲区满。操作和循环队列类似。
程序的源代码如下:1、头文件
驱动程序中所需要的头文件如下:
2、全局变量
本驱动程序用到的全局变量
3、设备结构体定义
设备结构体中定义了设备的资源,包括等待队列,对于设备来说也是一种资源,信号也是设备的资源。
4、open函数
open函数中对于一些只有open时才用到的资源进行初始化和分配,并不
一定要在加载函数中分配。
5、close函数
6、read函数
本函数当中用到了简单睡眠机制。调用宏wait_event_interruptible
下面是数据的读入操作
其中min是内核中定义的宏,取得两者之间的一个最小值
7、write函数
进入之前,首先获得信号量,判断缓冲区是否可写。
8、手动睡眠实现
这个函数在本驱动程序中主要是获得当前缓冲区是否可以写,若是不可以写,则进入等待队列睡眠,而这个过程是利用手动睡眠来实现的。
注意:在执行完prepare_to_wait之后,要调用内核调度核心函数,实现进程切换,因为当前进程已经是可中断的等待态。
9、判断缓冲区是否满
10、file_operations结构体
11、初始化cdev
12、加载函数
13、卸载函数
释放资源的顺序和申请资源的顺序正好相反
因篇幅问题不能全部显示,请点此查看更多更全内容