源出处:http://www.startos.com/linux/tips/2011011921499_4.html
2.3.3 prepare_to_wait和finish_wait
/*
* Used to distinguish between sync and async io wait context:
* sync i/o typically specifies a NULL wait queue entry or a wait
* queue entry bound to a task (current task) to wake up.
* aio specifies a wait queue entry with an async notification
* callback routine, not associated with any task.
*/
#define is_sync_wait(wait) (!(wait) || ((wait)->private))
同步io等待将唤醒当前进程,异步io等待和当前进程无关,时间到后执行安装的回调函数
void fastcall
prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;
wait->flags &= ~WQ_FLAG_EXCLUSIVE; //非排它性的等待将都被唤醒
spin_lock_irqsave(&q->lock, flags);
//等待节点尚未添加到任何等待队列中,防止重复加入
if (list_empty(&wait->task_list))
__add_wait_queue(q, wait);
if (is_sync_wait(wait))
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);
}
prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state)
wait->flags |= WQ_FLAG_EXCLUSIVE;
排他性等待,其余和prepare_to_wait一样
void fastcall finish_wait(wait_queue_head_t *q, wait_queue_t *wait)
{
unsigned long flags;
__set_current_state(TASK_RUNNING); //确保进程状态为running
//若有等待进程,则将其从等待队列中删除
if (!list_empty_careful(&wait->task_list)) {
spin_lock_irqsave(&q->lock, flags);
list_del_init(&wait->task_list);
spin_unlock_irqrestore(&q->lock, flags);
}
}
3 等待事件event
Linux 内核中最简单的休眠方式是称为 wait_event的宏(及其变种),它实现了休眠和进程等待的条件的检查。形式如下:
wait_event(queue, condition)/*不可中断休眠,不推荐*/ wait_event_interruptible(queue, condition)/*推荐,返回非零值意味着休眠被中断,且驱动应返回 -ERESTARTSYS*/ wait_event_timeout(queue, condition, timeout) wait_event_interruptible_timeout(queue, condition, timeout) /*有限的时间的休眠;若超时,则不管条件为何值返回0,*/
上述四个宏函数为内核对外的接口,其他的休眠函数应避免使用。因为宏并不是函数,参数所做的任何修改对调用环境仍然有效,所以queue都是“值传递”,在宏内部会调用底层函数,采用的指针传递。Linux内核中存在大部分这样的宏,其都在接口上都是值传递。
3.1 wait_event
认真地看简单休眠中的 wait_event(queue, condition) 和 wait_event_interruptible(queue, condition) 底层源码会发现,其实他们只是手工休眠中的函数的组合。因此在驱动程序中应避免使用手动休眠代码,而应该采用内核已经封装好的四个wait_event系列函数。
\include\linux\wait.h
#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)
// “__”表示内部函数,默认为condition不满足,添加至等待队列,调度
注意prepare_to_wait和finish_wait的匹配关系
#define wait_event(wq,condition) \
do { \
if(condition) \
break; \
__wait_event(wq,condition); \
}while (0)
// 对外的接口函数,需要判断condition,若假则等待;若真则直接退出
--------------------------------------------------------------------------------------------------------------
等待系列函数架构设计:
3.2 wait_event_timeout
#define __wait_event_timeout(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \
if (condition) \
break; \
ret = schedule_timeout(ret); \
if (!ret) \
break; //延时到,退出 \
} \
finish_wait(&wq, &__wait); \
} while (0)
#define wait_event_timeout(wq,condition,timeout) \
({ \
long __ret=timeout; \
if( !(condition) ) \
__wait_event_timeout( wq,condition,__ret); \
__ret; \
})
3.3 wait_event_interruptible
#define __wait_event_interruptible(wq, condition, ret) \
do { \
DEFINE_WAIT(__wait); \
\
for (;;) { \
prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
if (!signal_pending(current)) { \
schedule(); \
continue; \
} \
ret = -ERESTARTSYS; \
break; \
} \
finish_wait(&wq, &__wait); \
} while (0)