通常来讲,当读取,一个设备时,没有可读数据,可以阻塞该读进程,等待写进程写入数据。Linux提供了等待队列来维护一些阻塞(休眠)进程。
等待队列就是一个进程链表,包含了等待某个特定事件的所有进程。等待队列三通过一个等待队列头来管理这个队列的。这个头类型为:wait_queque_head_t,定义在<linux/wait.h>。可以通过下面的宏来初始化一个等待队列头:
DECLARE_WAIT_QUEQUE_HEAD(name);
也可以动态的定义一个等待队列头:
wait_queue_head_tmy_queue;
init_waitqueue_head(&my_queue);
有了头以后,也就是说我们已经用有了一个等待队列,还缺少关于等待队列的entry了。下面我们要初始化一个等待队列entry:
DEFINE_WAIT(my_wait);
或者用动态的方式:
wait_queue_tmy_wait;
init_wait(&my_wait);
接下来,我们要把这个entyr加到等待队列中去,并且设置进程的状态。prepare_to_wait帮助我们完成这项任务。
voidprepare_to_wait(wait_queue_head_t *queue,wait_queue_t *wait,int state);
到了这步后,我们可以执行schedule();来调度进程了。当schedule返回了,可以调用finish_wait()来把作清除工作了。finish_wait原型:
voidfinish_wait(wait_queue_head_t *queue,wait_queue_t *wait);
执行调度后,也就宣告我们把一个进程放到了等待队列上了,这个entry会等待我们去唤醒它。关于唤醒函数,是有几个变种的,下面尽量概括齐全。
wake_up(wait_queue_head_t*queue);//唤醒等待队列上非独占等待的进程
wake_up_interruptible(wait_queue_head_t*queue);//唤醒等待队列上可中断的等待进程
wake_up_nr(wait_queue_head_t*queue,int nr);//唤醒等待队列上的nr个进程
wake_up_interruptible_nr(wait_queue_head_t*queue,int nr);// 唤醒等待队列上可中断的nr个等待进程(当nr为0,表示唤醒所有)
wake_up_all(wait_queue_head_t*queue);//唤醒所有进程,无论是否为独占等待
wake_up_interruptible_all(wait_queue_head_t*queue);//唤醒所有可中断进程,无论是否为独占等待
wake_up_interruptible_sync(wait_queue_head_t*queue);//在唤醒进程的同时,强制重新调度
对于上面几个唤醒函数,一般也就用到wake_up_interruptible()。
假设有一个读进程,一个写进程。等待队列在程序代码的实现一般如下:
int XXX_read(…)
{
wait_queue_head_t my_queue;
wait_queue_t my_wait;
init_waitqueue_head(&my_queue);
init_wait(&my_wait);
prepare_to_wait(&my_queue,&my_wait,TASK_INTERRUPTIBLE);
if(STATE_EMPTY)
schedule();
finish_wait(&my_queue);
}
int XXX_write()
{
if(!STATE_EMPTY)
wake_up_interruptible(&my_queue);
}
大概就是这样一个思路,数据空时(STATE_EMPTY为真),让读进程进入等待队列。数据非空时,唤醒等待队列,schedule返回,接着执行读进程。具体的细节,自己琢摩去。