1. Linux中很多同步机制,completion是其中一种,其核心数据结构相对简单
include/linux/completion.h
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
常用的接口函数:
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
extern void complete(struct completion *);
extern void wait_for_completion(struct completion *);
在本文中,主要讲述complete 和 wait_for_completion,其余的可以在此基础掌握。
2. wait_for_completion(struct completion *)
kernel/sched/core.c
/**
* wait_for_completion: - waits for completion of a task
* @x: holds the state of this particular completion
*
* This waits to be signaled for completion of a specific task. It is NOT
* interruptible and there is no timeout.
*
* See also similar routines (i.e. wait_for_completion_timeout()) with timeout
* and interrupt capability. Also see complete().
*/
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); //设置当前进程的状态TASK_UNINTERRUPTIBLE。
spin_unlock_irq(&x->wait.lock);
timeout = action(timeout); //调度和返回
spin_lock_irq(&x->wait.lock);
} while (!x->done && timeout); // 检查x-》done 是否大于零,是不是有其它进程设置了该变量。
__remove_wait_queue(&x->wait, &wait);
if (!x->done)
return timeout;
}
x->done--;
return timeout ?: 1;
}
总之,wait_for_completion的作用是使当前进程处于TASK_UNINTERRUPTIBLE,处于等待队列,不再运行。进一步说,就是该函数只能在有进程上下文的情况下调用。
3. complete(struct completion *)
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++; // 设置x->done,
__wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL); //唤醒等待进程。
spin_unlock_irqrestore(&x->wait.lock, flags);
}
EXPORT_SYMBOL(complete);
非常简单易用的同步机制。