如果在内核中一个任务需要发出信号通知另一个任务发生了某个特定事件,利用完成变量(complection variable)是使两个任务得以同步的简单方法。如果一个任务要执行一些工作时,另一个任务就会在完成变量上等待。当这个任务完成工作后,会使用完成变量去唤醒在等待的任务。
- 在<Completion.h(include/linux)>中
- struct completion {
- unsigned int done;
- wait_queue_head_t wait;
- };
静态创建并初始化完成变量:
- #define COMPLETION_INITIALIZER(work) /
- { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
- #define DECLARE_COMPLETION(work) /
- struct completion work = COMPLETION_INITIALIZER(work)
- 在<Wait.h(include/linux)>中
- #define __WAIT_QUEUE_HEAD_INITIALIZER(name) { /
- .lock = __SPIN_LOCK_UNLOCKED(name.lock), /
- .task_list = { &(name).task_list, &(name).task_list } }
动态创建并初始化完成变量:
- static inline void init_completion(struct completion *x)
- {
- x->done = 0;
- init_waitqueue_head(&x->wait);
- }
在<Wait.c(kernel)>中
- void init_waitqueue_head(wait_queue_head_t *q)
- {
- spin_lock_init(&q->lock);
- INIT_LIST_HEAD(&q->task_list);
- }
在一个指定的完成变量上,需要等待的任务调用wait_for_completion()来等待特定事件。当特定事件发生后,产生事件的任务调用completion()来发送信号唤醒正在等待的任务。
- 在<sched.h(kernel)>中:
- void fastcall __sched wait_for_completion(struct completion *x)
- {
- might_sleep();
- spin_lock_irq(&x->wait.lock);
- if (!x->done) {
- DECLARE_WAITQUEUE(wait, current);
- wait.flags |= WQ_FLAG_EXCLUSIVE;
- __add_wait_queue_tail(&x->wait, &wait);
- do {
- __set_current_state(TASK_UNINTERRUPTIBLE);
- spin_unlock_irq(&x->wait.lock);
- schedule();
- spin_lock_irq(&x->wait.lock);
- } while (!x->done);
- __remove_wait_queue(&x->wait, &wait);
- }
- x->done--;
- spin_unlock_irq(&x->wait.lock);
- }
在<sched.h(kernel)>中:
- void fastcall complete(struct completion *x)
- {
- unsigned long flags;
- spin_lock_irqsave(&x->wait.lock, flags);
- x->done++;
- __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
- 1, 0, NULL);
- spin_unlock_irqrestore(&x->wait.lock, flags);
- }
- EXPORT_SYMBOL(complete);
- void fastcall complete_all(struct completion *x)
- {
- unsigned long flags;
- spin_lock_irqsave(&x->wait.lock, flags);
- x->done += UINT_MAX/2;
- __wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,
- 0, 0, NULL);
完成变量的通常用法是,将完成变量作为数据结构中的一项动态创建,而完成数据结构初始化工作的内核代码将调用wait_for_completion()进行等待。使用完成变量的例子可以参考kernel/sched.c和kernel/fork.c。