您的位置:首页 > 运维架构 > Linux

Linux 内核等待队列

2017-03-08 19:12 381 查看
Linux内核中的等待队列是一种延时机制,其用于当前进程需要等待某些资源而进入一种sleep状态,当等待条件为真时,

进程被唤醒,继续执行。显然,这里涉及三个方面,即,一是等待时当前进程处理,

二是进程等待时所关注的资源处理,三时进程何时被唤醒继续执行。

所以,我们这里需要几个数据结构,主要描述如下:

typedefint(*wait_queue_func_t)(wait_queue_t*wait,unsignedmode,intflags,void*key);


struct__wait_queue{
unsignedintflags;
void*private;
wait_queue_func_tfunc;
structlist_headtask_list;
};

typedefstruct__wait_queuewait_queue_t;

此结构用于把当前进程进入睡眠时保持状态信息,


struct__wait_queue_head{
spinlock_tlock;
structlist_headtask_list;
};
typedefstruct__wait_queue_headwait_queue_head_t;

此结构用于定义一个等待队列,task_list是在等待队列上的任务,即等待任务的状态信息,即链表上是

struct__wait_queue对象。


对于等待队列中的数据结构,有时也可以使用下面这个:

structwait_bit_key{
void*flags;
intbit_nr;
#defineWAIT_ATOMIC_T_BIT_NR-1
unsignedlongtimeout;
};
structwait_bit_queue{
structwait_bit_keykey;
wait_queue_twait;
};

对于等待队列头来说,可以通过下面方式初始化:
#define__WAIT_QUEUE_HEAD_INITIALIZER(name){\
.lock=__SPIN_LOCK_UNLOCKED(name.lock),\
.task_list={&(name).task_list,&(name).task_list}}
#defineDECLARE_WAIT_QUEUE_HEAD(name)\
wait_queue_head_tname=__WAIT_QUEUE_HEAD_INITIALIZER(name)

而对于等待队列中的成员来说,其因为使用了struct__wait_queue结构,所以,
一般对等待队列中成员定义如下:
#define__WAITQUEUE_INITIALIZER(name,tsk){\
.private=tsk,\
.func=default_wake_function,\
.task_list={NULL,NULL}}
#defineDECLARE_WAITQUEUE(name,tsk)\
wait_queue_tname=__WAITQUEUE_INITIALIZER(name,tsk)

或者其他一些形式定义,如下:
#define__WAIT_BIT_KEY_INITIALIZER(word,bit)\
{.flags=word,.bit_nr=bit,}
#define__WAIT_ATOMIC_T_KEY_INITIALIZER(p)\
{.flags=p,.bit_nr=WAIT_ATOMIC_T_BIT_NR,}

#defineinit_waitqueue_head(q)\
do{\
staticstructlock_class_key__key;\
\
__init_waitqueue_head((q),#q,&__key);\
}while(0)

简单函数形式如下:
staticinlinevoidinit_waitqueue_entry(wait_queue_t*q,structtask_struct*p)
{
q->flags=0;
q->private=p;
q->func=default_wake_function;带唤醒函数
}
staticinlinevoid
init_waitqueue_func_entry(wait_queue_t*q,wait_queue_func_tfunc)
{
q->flags=0;
q->private=NULL;
q->func=func;设置唤醒函数,但任务未设置
}
简单的加入队列过程:
staticinlinevoid__add_wait_queue(wait_queue_head_t*head,wait_queue_t*new)
{
list_add(&new->task_list,&head->task_list);
}

对外简单封装函数有:
一.
voidadd_wait_queue(wait_queue_head_t*q,wait_queue_t*wait)
{
unsignedlongflags;
wait->flags&=~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock,flags);
__add_wait_queue(q,wait);
spin_unlock_irqrestore(&q->lock,flags);
}
EXPORT_SYMBOL(add_wait_queue);
二.
void
prepare_to_wait(wait_queue_head_t*q,wait_queue_t*wait,intstate)
{
unsignedlongflags;
wait->flags&=~WQ_FLAG_EXCLUSIVE;
spin_lock_irqsave(&q->lock,flags);
if(list_empty(&wait->task_list))
__add_wait_queue(q,wait);
set_current_state(state);
spin_unlock_irqrestore(&q->lock,flags);
}
EXPORT_SYMBOL(prepare_to_wait);

三.
longprepare_to_wait_event(wait_queue_head_t*q,wait_queue_t*wait,intstate)
{
unsignedlongflags;
longret=0;
spin_lock_irqsave(&q->lock,flags);
if(unlikely(signal_pending_state(state,current))){
/*
*Exclusivewaitermustnotfailifitwasselectedbywakeup,
*itshould"consume"theconditionwewerewaitingfor.
*
*Thecallerwillrechecktheconditionandreturnsuccessif
*wewerealreadywokenup,wecannotmisstheeventbecause
*wakeuplocks/unlocksthesameq->lock.
*
*Butweneedtoensurethatset-condition+wakeupafterthat
*can'tseeus,itshouldwakeupanotherexclusivewaiterif
*wefail.
*/
list_del_init(&wait->task_list);
ret=-ERESTARTSYS;
}else{
if(list_empty(&wait->task_list)){
if(wait->flags&WQ_FLAG_EXCLUSIVE)
__add_wait_queue_tail(q,wait);
else
__add_wait_queue(q,wait);
}
set_current_state(state);
}
spin_unlock_irqrestore(&q->lock,flags);
returnret;
}
四.
staticinlinevoid
__add_wait_queue_exclusive(wait_queue_head_t*q,wait_queue_t*wait)
{
wait->flags|=WQ_FLAG_EXCLUSIVE;
__add_wait_queue(q,wait);
}

等待队列的简单使用:
一,定义等待队列头对象
如staticDECLARE_WAIT_QUEUE_HEAD(nl_table_wait);
二.等待资源时,把当前进程加入等待队列,队列成员
voidnetlink_table_grab(void)__acquires(nl_table_lock)
{
might_sleep();
write_lock_irq(&nl_table_lock);
if(atomic_read(&nl_table_users)){
DECLARE_WAITQUEUE(wait,current);自定义队列成员,并简单初始化
add_wait_queue_exclusive(&nl_table_wait,&wait);只是进入队列,但进程还可以运行
for(;;){
set_current_state(TASK_UNINTERRUPTIBLE);//改变进程状态
if(atomic_read(&nl_table_users)==0)必须满足这个条件,否则不会被唤醒
break;//唤醒后,跳出
write_unlock_irq(&nl_table_lock);
schedule();这一步放弃CPU,真正调度出去,回来也是从这里开始
write_lock_irq(&nl_table_lock);//再次回来时,获取锁
}
__set_current_state(TASK_RUNNING);//设置可运行
remove_wait_queue(&nl_table_wait,&wait);//把之前设置释放。
}
}

三.唤醒路径
voidnetlink_table_ungrab(void)__releases(nl_table_lock)
{
write_unlock_irq(&nl_table_lock);
wake_up(&nl_table_wait);

}





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: