摘自 <<Linux设备驱动开发详解>>(第三版)
在 Linux 驱动程序中,可以使用等待队列(Wait Queue) 来实现阻塞进程的唤醒.等待队列很早就作为一个基本的功能单位出现在 Linux 内核里了,它以队列为基础数据结构,与进程调试机制紧密结合,可以用来同步对系统资源的访问.
Linux 内核提供了如下关于等待队列的操作.
1.定义"等待队列头部"
wait_queue_head_t my_queue;
wait_queue_head_t 是 __wait_queue_head 结构体的一个 typedef
ps:
include/linux/wait.h :
typedef struct __wait_queue_head wait_queue_head_t;
2.初始化"等待队列头部"
init_waitqueue_head(&my_queue);
使用宏定义并初始化等待队列头部:
DECLARE_WAIT_QUEUE_HEAD(name)
3.定义等待队列元素
DECLARE_WAITQUEUE(name, tsk)
定义并初始化一个名为 name 的等待队列元素
4.添加/移除等待队列
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
add_wait_queue() 用于将等待队列元素 wait 添加到等待队列头部 q 指向的双向链表中,而 remove_wait_queue() 用于将等待队列元素 wait 从由 q 头部指向的链表中移除.
5.等待事件
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
第 1 个参数 queue 作为等待队列头部的队列被唤醒,而且第 2 个参数 condition 必须满足,否则继续阻塞. wait_event() 和 wait_event_interruptible() 的区别在于后者可以被信号打断,而前者不能.加上 _timeout 后綴意味着阻塞等待的超时时间, 以 jiffy 为单位, 在弟 3 个参数的 timeout 到达时, 不论 condition 是否满足, 均返回.
6.唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
上述操作会唤醒以 queue 作为等待队列头部的队列中所有的进程
wake_up() 应该与 wait_event() 或 wait_event_timeout() 成对使用, 而 wake_up_interruptible() 应该与 wait_event_interruptible() 或 wait_event_interruptible_timeout() 成对使用. wake_up() 可唤醒处于 TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE 的进程, 而 wake_up_interruptible() 只能唤醒处于 TASK_INTERRUPTIBLE 的进程.
7.在等待队列上睡眠
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
sleep_on() 函数的作用就是将目前进程的状态置成 TASK_UNINTERRUPTIBLE, 并定义一个等待队列元素, 之后把它挂到等待队列头部 q 指向的双向链表, 直到资源可获得, q 队列指向链接的进程被唤醒.
interruptible_sleep_on() 与 sleep_on() 函数类似, 其作用是将目前进程的状态置成 TASK_INTERRUPTIBLE, 并定义一个等待队列元素, 之后把它附属到 q 指向的队列, 直到资源可获得 (q 指引的等待队列被唤醒) 或者进程收到信号.
sleep_on() 函数应该与 wake_up() 成对使用, interruptible_sleep_on() 应该与 wake_up_interruptible() 成对使用.