android 驱动中的并发和竞争——completion

很多时候在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);
}


至此关于completion的用法告一段落,下一篇会分析自旋锁的使用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值