浅析Linux等待队列
(转载) bojan 收录于2010-10-09 阅读数: 公众公开
我也要收藏
在Linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。等待很早就作为一个基本的功能单位出现在Linux内核中,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制。
我们从它的使用范例着手,看看等待队列是如何实现异步信号功能的。以下代码节选自kernel/printk.c。
DECLARE_WAIT_QUEUE_HEAD(log_wait); // 初始化等待队列头log_wait
static DEFINE_SPINLOCK(logbuf_lock); // 定义自旋锁logbuf_lock
int do_syslog(int type, char __user *buf, int len)
{
... ... ...
err = wait_event_interruptible(log_wait, (log_start - log_end)); // 等待
/*
// 我们先来看看linux/wait.h中定义的wait_event_interruptible()的原型
#define wait_event_interruptible(wq, condition)
({
int __ret = 0;
if ( (!condition) )
__wait_event_interruptible(wq, condition, __ret);
__ret;
})
// wait_event_interruptible()将等待队列加入第一个参数wq为等待队列头的等待队列链表中,
// 当condition满足时,wait_event_interruptible()会立即返回,否则,阻塞等待condition满足。
// 与之类似的还有wait_event(),不同点在于wait_event_interruptible()可以被信号唤醒。
// __wait_event_interruptible()原型如下:
#define __wait_event_interruptible(wq, condition, __ret)
do {
DEFINE_WAIT(__wait); // 定义等待队列__wait
for(;;) {
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);
// prepare_to_wait()的作用是:将等待队列__wait加入以wq为等待队列的等待队列
// 链表中,并且将进程状态设置为TASK_INTERRUPTIBLE
if (condition)
break; // 如果condition满足则跳出
if (!signal_pending(current)) { // 如果不是被信号唤醒
ret = schedule_timeout(ret);
if (!ret)
break;
continue;
}
ret = - ERESTARTSYS;
break;
schedule(); // 放弃CPU,调度其它进程执行
}
finish_wait(&wq, &__wait);
// finish_wait()的作用是,将等待队列__wait从等待队列头wq指向的等待队列链表中移除,
// 并且将进程状态设置为TASK_RUNNING
}while(0)
*/
... ... ...
spin_lock_irq(&lock_wait); // 锁定
... ... ...
log_start++; // log_start自加,使得(log_start - log_end)这个等待队列的condition满足
... ... ...
spin_unlock_irq(&lock_wait); // 解锁
... ... ...
}
void wake_up_klogd(void)
{
... ... ...
wake_up_interruptible(&lock_wait); //唤醒等待队列
}
void release_console_sem(void)
{
... ... ...
spin_lock_irqsave(&lock_wait); // 锁定
wake_klogd = log_start - log_end;
... ... ...
spin_unlock_irqrestore(&lock_wait); // 解锁
... ... ...
if (wake_klogd)
wake_up_klogd(); // 如果wake_klogd非负,则调用wake_up_klogd()来唤醒等待队列
... ... ...
}
我们最后再总结一下等待队列wait_event_interruptible():
在wait_event_interruptible()中首先判断conditon是不是已经满足,如果是则直接返回0,否则调用__wait_event_interruptible(),并用__ret来存放返回值。__wait_event_interruptible()首先将定义并初始化一个wait_queue_t变量__wait,其中数据为当前进程状态current(struct task_struct),并把__wait加入等待队列。在无限循环中,__wait_event_interruptible()将本进程置为可中断的挂起状态,反复检查condition是否成立,如果成立则退出,如果不成立则继续休眠;条件满足后,即把本进程运行状态置为运行态,并把__wait从等待队列中清除,从而进程能够调度运行。
挂起的进程并不会自动转入运行的,因此,还需要一个唤醒动作,这个动作由wake_up_interruptible()完成,它将遍历作为参数传入的lock_wait等待队列,将其中所有的元素(通常都是task_struct)置为运行态,从而可被调度。