很多时候在linux系统中,某些进程的运行需要确定某进程A被其他进程调用完成之后才能继续,
因此通常在进程A中定义一个结构体completion用来标识该进程是否完成,或者是存储等待该进程的其余进程队列。
2、Completion
先来看一下关于completion的定义;
struct completion {
unsigned int done; //判断该进程是否完成的标示符
wait_queue_head_t wait; //存储等待该进程A的等待队列的头指针
};
定义并初始化的completion结构体,done设为0并初始化等待队列:
#define DECLARE_COMPLETION(work) struct completion work = COMPLETION_INITIALIZER(work)
#define COMPLETION_INITIALIZER(work) { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
或者是初始化一个已经存在的completion结构体:
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
接下来看看几种相关的调用:
(1)不可中断且没有超时限制的等待
void __sched wait_for_completion(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE, 0);
}
(2)超时等待,不可中断
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE, 0);
}
若成功等待进程A被其他进程调用结束则返回剩余的可等待时间,否则返回0。
(3)可中断的等待,没有与超时限制int __sched wait_for_completion_interruptible(struct completion *x)
{
long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT,TASK_INTERRUPTIBLE, 0);
if (t == -ERESTARTSYS)
return t;
return 0;
}
若成功等待进程A结束则返回0,否则返回-ERESTSTRTSYS信号。
(4)可中断且有时间限制的等待
wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_INTERRUPTIBLE, 0);
}
若成功等待进程A被其他进程结束则返回剩余的可等待时间,否则返回0。(5)可以被kill掉进程的等待
int __sched wait_for_completion_killable(struct completion *x)
{
long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE, 0);
if (t == -ERESTARTSYS)
return t;
return 0;
}
如果成功等待进程A被其他进程结束则返回0,否则返回-ERESTSRTSYS信号。
(6)可以被kill掉且有超时限制的等待
wait_for_completion_killable_timeout(struct completion *x,
unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_KILLABLE, 0);
}
若成功等待进程A被其他进程结束则返回剩余的可等待时间,否则返回0。(7)
void __sched wait_for_completion_io(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE, 1);
}
若成功等待进程A被其他进程结束则返回剩余的可等待时间,否则返回0。使用这个函数时,为等待进程A结束而休眠的进程都会被当时是等待一个IO,目的是计算进程数(翻译代码中的解释)
(8)不会引起休眠的等待
bool try_wait_for_completion(struct completion *x)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&x->wait.lock, flags);
if (!x->done)
ret = 0;
else
x->done--;
spin_unlock_irqrestore(&x->wait.lock, flags);
return ret;
}
如果进程A被其他进程结束的话,done减1(说明进程A重新被调用)且函数返回1,
否则返回0,需要等待A结束的进程不会添加到等待队列。
bool completion_done(struct completion *x)
{
unsigned long flags;
int ret = 1;
spin_lock_irqsave(&x->wait.lock, flags);
if (!x->done)
ret = 0;
spin_unlock_irqrestore(&x->wait.lock, flags);
return ret;
}
当进程A未被其他进程调用结束则返回0,否则返回1。
调用等待函数都介绍完了,下面看一下进程A宣布自己的进程结束时completion会进行哪些操作。
(1)唤醒单个进程
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags);
x->done++;
__wake_up_common(&x->wait, TASK_NORMAL, 1, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}
先令done++,说明多一条可用的已经被调用结束的进程A,然后按先入先出的顺序唤醒等待队列中的单个进程
(2)唤醒所有进程void 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_NORMAL, 0, 0, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags);
}