声明:本文以网上不知名作者为基础加以修改,在此先谢一声!红色部分为我写程序的时候用到的函数。
一、定义:
/include/linux/wait.h
struct spinlock_t struct }; typedef |
二、作用:
在内核里面,等待队列是有很多用处的,尤其是在中断处理、进程同步、定时等场合
。可以使用等待队列在实现阻塞进程的唤醒。它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的
异步事件通知机制
,同步对系统资源的访问等。
三、字段详解:
1
、
spinlock_t
lock;
在对
task_list
与操作的过程中,使用该锁实现对等待队列的互斥访问。
2
、
srtuct
list_head_t task_list;
双向循环链表,存放等待的进程。
三、操作:
1
、定义并初始化:
(1)
wait_queue_head_t
my_queue;
init_waitqueue_head(&my_queue);
直接定义并初始化。
init_waitqueue_head()
函数会将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表。
(2)
DECLARE_WAIT_QUEUE_HEAD(my_queue);
定义并初始化,相当于
(1)
。
(3)
定义等待队列:
DECLARE_WAITQUEUE(name,tsk);
注意此处是定义一个
wait_queue_t
类型的变量
name
,并将其
private
与设置为
tsk
。
wait_queue_t
类型定义如下:
|
其中
flags
域指明该等待的进程是互斥进程还是非互斥进程。其中
0
是非互斥进程,
WQ_FLAG_EXCLUSIVE(0x01)
是互斥进程。等待队列
(wait_queue_t)
和等待对列头
(wait_queue_head_t)
的区别是等待队列是等待队列头的成员。也就是说等待队列头的
task_list
域链接的成员就是等待队列类型的
(wait_queue_t)
。
2
、
(
从等待队列头中
)
添加/移出等待队列:
(1)add_wait_queue()
函数:
void { unsigned
wait->flags
spin_lock_irqsave(&q->lock, __add_wait_queue(q,
spin_unlock_irqrestore(&q->lock, } |
设置等待的进程为非互斥进程,并将其添加进等待队列头
(q)
的队头中。
void { unsigned
wait->flags
spin_lock_irqsave(&q->lock,
__add_wait_queue_tail(q,
spin_unlock_irqrestore(&q->lock, } |
该函数也和
add_wait_queue()
函数功能基本一样,只不过它是将等待的进程
(wait)
设置为互斥进程。
(2)remove_wait_queue()
函数:
void { unsigned
spin_lock_irqsave(&q->lock,
__remove_wait_queue(q,
spin_unlock_irqrestore(&q->lock, } |
在等待的资源或事件满足时,进程被唤醒,使用该函数被从等待头中删除。
3
、等待事件:
(1)wait_event()
宏:
#define
do if break; __wait_event(wq, }
#define do DEFINE_WAIT(__wait);
/ for
prepare_to_wait(&wq, if
break; ret if
break; } finish_wait(&wq,
} |
在等待会列中睡眠直到
condition
为真。在等待的期间,进程会被置为
TASK_UNINTERRUPTIBLE
进入睡眠,直到
condition
变量变为真。每次进程被唤醒的时候都会检查
condition
的值
.
(2)wait_event_interruptible()
函数
:
和
wait_event()
的区别是调用该宏在等待的过程中当前进程会被设置为
TASK_INTERRUPTIBLE
状态
.
在每次被唤醒的时候
,
首先检查
condition
是否为真
,
如果为真则返回
,
否则检查如果进程是被信号唤醒
,
会返回
-ERESTARTSYS
错误码
.
如果是
condition
为真
,
则返回
0.
(3)wait_event_timeout()
宏
:
也与
wait_event()
类似
.
不过如果所给的睡眠时间为负数则立即返回
.
如果在睡眠期间被唤醒
,
且
condition
为真则返回剩余的睡眠时间
,
否则继续睡眠直到到达或超过给定的睡眠时间
,
然后返回
0.
(4)wait_event_interruptible_timeout()
宏
:
与
wait_event_timeout()
类似
,
不过如果在睡眠期间被信号打断则返回
ERESTARTSYS
错误码
.
(5)
wait_event_interruptible_exclusive()
宏
同样和
wait_event_interruptible()
一样
,
不过该睡眠的进程是一个互斥进程
.
5
、唤醒队列
:
(1)出
wake_up()
函数
:
#define
void int { unsigned
spin_lock_irqsave(&q->lock, __wake_up_common(q,
spin_unlock_irqrestore(&q->lock, } static
int nr_exclusive, int sync, { struct
list_for_each_safe(tmp, wait_queue_t unsigned
if
(flags &
break; } } |
唤醒等待队列
.
可唤醒处于
TASK_INTERRUPTIBLE
和
TASK_UNINTERUPTIBLE
状态的进程
,
和
wait_event/wait_event_timeout
成对使用
.
(2)wake_up_interruptible()
函数
:
#define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) |
和
wake_up()
唯一的区别是它只能唤醒
TASK_INTERRUPTIBLE
状态的进程
.,
与
wait_event_interruptible/wait_event_interruptible_timeout/
wait_event_interruptible_exclusive
成对使用
.
(3)
#define #define #define |
这些也基本都和
wake_up/wake_up_interruptible
一样
.
6
、在等待队列上睡眠:
(1)sleep_on()
函数:
void __sched sleep_on(wait_queue_head_t *q) { |
该函数的作用是定义一个等待队列
(wait)
,并将当前进程添加到等待队列中
(wait)
,然后将当前进程的状态置为
TASK_UNINTERRUPTIBLE
,并将等待队列
(wait)
添加到等待队列头
(q)
中。之后就被挂起直到资源可以获取,才被从等待队列头
(q)
中唤醒,从等待队列头中移出。在被挂起等待资源期间,该进程不能被信号唤醒。
(2)sleep_on_timeout()
函数:
long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout) |
与
sleep_on()
函数的区别在于调用该函数时,如果在指定的时间内
(timeout)
没有获得等待的资源就会返回。实际上是调用
schedule_timeout()
函数实现的。值得注意的是如果所给的睡眠时间
(timeout)
小于
0
,则不会睡眠。该函数返回的是真正的睡眠时间。
(3)interruptible_sleep_on()
函数:
void { unsigned wait_queue_t
init_waitqueue_entry(&wait,
current->state
sleep_on_head(q, schedule(); sleep_on_tail(q, } |
该函数和
sleep_on()
函数唯一的区别是将当前进程的状态置为
TASK_INTERRUPTINLE
,这意味在睡眠如果该进程收到信号则会被唤醒。
(4)interruptible_sleep_on_timeout()
函数:
long interruptible_sleep_on_timeout(wait_queue_head_t { unsigned wait_queue_t
init_waitqueue_entry(&wait,
current->state
sleep_on_head(q, timeout sleep_on_tail(q,
return } |
类似于
sleep_on_timeout()
函数。进程在睡眠中可能在等待的时间没有到达就被信号打断而被唤醒,也可能是等待的时间到达而被唤醒。
以上四个函数都是让进程在等待队列上睡眠,不过是小有诧异而已。在实际用的过程中,根据需要选择合适的函数使用就是了。例如在对软驱数据的读写中,如果设备没有就绪则调用
sleep_on()
函数睡眠直到数据可读
(
可写
)
,在打开串口的时候,如果串口端口处于关闭状态则调用
interruptible_sleep_on()
函数尝试等待其打开。在声卡驱动中,读取声音数据时,如果没有数据可读,就会等待足够常的时间直到可读取。