linux内核中等待队列

根据内核3.1.6版本源码、书籍和网上资料,对几个函数进行分析

介绍这几个函数,不得不先介绍等待队列wait_queue_head_t与完成量completion。

等待队列用于使得进程等待某一特定事件的发生,无需频繁的轮询,进程在等待周期中睡眠,当时间发生后由内核自动唤醒。

完成量机制是基于等待队列的,内核利用该机制等待某一操作的结束。这两种经常被使用。

一、等待队列

(一)数据结构

等待队列结构如下,因为每个等待队列都可以再中断时被修改,因此,在操作等待队列之前必须获得一个自旋锁。

[cpp]view plaincopyprint?

    struct__wait_queue_head{spinlock_tlock;structlist_headtask_list;};typedefstruct__wait_queue_headwait_queue_head_t;

等待队列是通过task_list双链表来实现,其数据成员是以下数据结构:[cpp]view plaincopyprint?

    typedefstruct__wait_queuewait_queue_t;struct__wait_queue{unsignedintflags;#defineWQ_FLAG_EXCLUSIVE0x01/*表示等待进程想要被独占地唤醒*/void*private;/*指向等待进程的task_struct实例*/wait_queue_func_tfunc;/*用于唤醒等待进程*/structlist_headtask_list;/*用于链表元素,将wait_queue_t链接到wait_queue_head_t*/};

其图如下:

等待队列如何使用哪?分两步

1. 为了使得等待进程在一个等待队列中睡眠,需要调用函数wait_event()函数。进程进入睡眠,将控制权释放给调度器。

2. 在内核中另一处,调用wake_up()函数唤醒等待队列中的睡眠进程。

注:使用wait_event()函数使得进程睡眠;而在内核另一处有一个对应的wake_up()函数被调用。 (二)初始化等待队列元素

有两种方法初始化队列:

1. 动态初始化init_waitqueue_entry()

[cpp]view plaincopyprint?

    staticinlinevoidinit_waitqueue_entry(wait_queue_t*q,structtask_struct*p){q->flags=0;q->private=p;q->func=default_wake_function;}

2. 静态初始化DEFINE_WAIT()[cpp]view plaincopyprint?

    #defineDEFINE_WAIT_FUNC(name,function)\wait_queue_tname={\.private=current,\.func=function,\.task_list=LIST_HEAD_INIT((name).task_list),\}#defineDEFINE_WAIT(name)DEFINE_WAIT_FUNC(name,autoremove_wake_function)

其中函数autoremove_wake_function()是用来唤醒进程的,该函数不经调用default_wake_function(),还将所属等待队列成员从等待队列删除。

(三)进程睡眠

1. 通过add_wait_queue()函数将一个进程添加到等待队列,首先获得自旋锁,然后调用__add_wait_queue()实现将新的等待进程添加等待队列(添加到等待队列的头部),然后解锁;代码如下:

[cpp]view plaincopyprint?

    staticinlinevoid__add_wait_queue(wait_queue_head_t*head,wait_queue_t*new){list_add(&new->task_list,&head->task_list);}

另一个函数add_wait_queue_exclusive()的含义与add_wait_queue()函数类似,但是将等待进程添加到等待队列的尾部,并设置WQ_EXCLUSIXE标志。

使得进程在等待队列上睡眠的另一种方法是:prepare_to_wait(),除了有add_wait_queue()函数的参数外,还要设置进程的状态。

另一个函数prepare_to_wait_exclusive()语义类似。

通常情况下,add_wait_queue()函数不会直接使用,而是调用wait_evnet()函数

[cpp]view plaincopyprint?

    /***wait_event-sleepuntilaconditiongetstrue*@wq:thewaitqueuetowaiton*@condition:aCexpressionfortheeventtowaitfor**Theprocessisputtosleep(TASK_UNINTERRUPTIBLE)untilthe*@conditionevaluatestotrue.The@conditionischeckedeachtime*thewaitqueue@wqiswokenup.**wake_up()hastobecalledafterchanginganyvariablethatcould*changetheresultofthewaitcondition.*/#definewait_event(wq,condition)\do{\if(condition)\break;\__wait_event(wq,condition);\}while(0)

函数__wait_event()[cpp]view plaincopyprint?

    #define__wait_event(wq,condition)\do{\DEFINE_WAIT(__wait);\\for(;;){\prepare_to_wait(&wq,&__wait,TASK_UNINTERRUPTIBLE);\if(condition)\break;\schedule();\}\finish_wait(&wq,&__wait);\}while(0)

其中wq是等待进程需要加入的等待队列,而condition是通过与所等待时间有关的一个C表达式形式给出。表示,条件满足时,可以立即停止处理。主要工作由__wait_event()来完成。

分析__wait_event()函数,

(1) 调用DEFINE_WAIT宏建立等待队列成员;

(2) 使用一个无线循环,在循环体内,

(a) 调用prepare_to_wait()使得进程在等待队列上等待;

(b) 当进程被唤醒时,检查指定的条件condition是否满足,如果满足则跳出循环,否则将控制权交给调度器,然后进程继续睡眠。

(3) 调用函数finish_wait()将进程状态设置为TASK_RUNNING,并从等待队列的链表中移除对应的成员。

其他与wait_event类似的函数:

1. wait_event_interruptible()函数 ,使得进程处于TASK_INTERRUPTIBLE状态,从而睡眠进程可以通过接收信号被唤醒;

2. wait_event_timeout()函数,等待满足指定的条件,但是如果等待时间超过指定的超时限制则停止睡眠,可以防止进程永远睡眠;

3. wait_event_interruptible_timeout() 使得进程睡眠,但是可以通过接收信号被唤醒,也具有超时限制。

(四)进程睡眠

内核中虽然定义了很多唤醒等待队列中进程的函数,但是最终调用的都是__wake_up()

[cpp]view plaincopyprint?

    #definewake_up(x)__wake_up(x,TASK_NORMAL,1,NULL)#definewake_up_nr(x,nr)__wake_up(x,TASK_NORMAL,nr,NULL)#definewake_up_all(x)__wake_up(x,TASK_NORMAL,0,NULL)#definewake_up_locked(x)__wake_up_locked((x),TASK_NORMAL)#definewake_up_interruptible(x)__wake_up(x,TASK_INTERRUPTIBLE,1,NULL)#definewake_up_interruptible_nr(x,nr)__wake_up(x,TASK_INTERRUPTIBLE,nr,NULL)#definewake_up_interruptible_all(x)__wake_up(x,TASK_INTERRUPTIBLE,0,NULL)#definewake_up_interruptible_sync(x)__wake_up_sync((x),TASK_INTERRUPTIBLE,1)

而__wake_up()函数在加锁之后调用的是__wake_up_common()

[html]view plaincopyprint?

    staticvoid__wake_up_common(wait_queue_head_t*q,unsignedintmode,intnr_exclusive,intwake_flags,void*key){wait_queue_t*curr,*next;list_for_each_entry_safe(curr,next,&q->task_list,task_list){unsignedflags=curr->flags;if(curr->func(curr,mode,wake_flags,key)&&(flags&WQ_FLAG_EXCLUSIVE)&&!–nr_exclusive)break;}}

其中:q是等待队列,mode指定进程的状态,用于控制唤醒进程的条件,nr_exclusive表示将要唤醒的设置了WQ_FLAG_EXCLUSIVE标志的进程的数目。

然后扫描链表,调用func,直至没有更多的进程被唤醒,或者被唤醒的的独占进程数目已经达到规定数目。

无论才能知识多么卓着,如果缺乏热情,则无异纸上画饼充饥,无补于事。

linux内核中等待队列

相关文章:

你感兴趣的文章:

标签云: