浅析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)置为运行态,从而可被调度。

posted on 2011-01-14 13:50  呵呵,呵呵  阅读(6805)  评论(1编辑  收藏  举报

导航