之前使用complete的时候,程序总是wait_for_completion函数先执行,并且每次只有一个wait_for_completion在等待,因此对于complete函数也没有太多的深入了解。后面再次需要使用这个功能的时候,想到如果wait_for_completion函数在complete之后执行会出现上面问题?
结论:如果wait_for_completion函数在complete之后执行,那么执行wait_for_completion函数时,添加就直接满足,不会再等待complete函数的执行。之所以是这样,是因为它实现的机制如下:
complete实现的函数如下:
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++; //这个变量很重要
__wake_up_locked(&x->wait, TASK_NORMAL, 1);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
wait_for_completion实现如下:
void __sched wait_for_completion(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion);
static long __sched
wait_for_common(struct completion *x, long timeout, int state)
{
return __wait_for_common(x, schedule_timeout, timeout, state);
}
static inline long __sched
__wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
might_sleep();
spin_lock_irq(&x->wait.lock);
timeout = do_wait_for_common(x, action, timeout, state);
spin_unlock_irq(&x->wait.lock);
return timeout;
}
static inline long __sched
do_wait_for_common(struct completion *x,
long (*action)(long), long timeout, int state)
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current);
__add_wait_queue_tail_exclusive(&x->wait, &wait);
do {
if (signal_pending_state(state, current)) {
timeout = -ERESTARTSYS;
break;
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock);
timeout = action(timeout);
spin_lock_irq(&x->wait.lock);
} while (!x->done && timeout);
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
x->done--; //complete函数里面自加的变量
return timeout ?: 1;
}
通过查看其实现的方式,发现complete实现的原理是:
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
通过判断done的值,来判断complete是否执行,执行了多少次。
complete函数执行一次,done就加1
wait_for_completion函数执行一次,done就减1
如果done等于0,那么wait_for_completion函数将一直等待下去。
下面附上complete的简单实用:
struct completion temp_completion; //定义一个变量
init_completion(&temp_completion); //初始化变量
complete(&temp_completion); //发送完成量 唤醒一个等待
wait_for_completion(&temp_completion); //等待完成量
complete_all(&temp_completion); //唤醒所有等待
wait_for_completion_timeout(&temp_completion, HZ); //等待可以超时
wait_for_completion_interruptible(&temp_completion); //等待可以被中断
wait_for_completion_interruptible_timeout(&temp_completion); //等待可以超时、被中断