完成量

版权声明:本文为博主原创文章,未经博主允许不得转载。
https://blog.csdn.net/huangweiqing80/article/details/83105960

在驱动程序开发中,一种常见的情况是:一个线程需要等待另一个线程执行完某个操作后,才能继续执行。这个工作其实信号量也可以完成,但其效率比Linux中专门针对这种情况的完成量机制要差些

一、完成量概述

Linux中提供了一种机制,实现一个线程发送一个信号通知另一个线程开始完成某个任务,这种机制就是完成量。完成量的目的是告诉一个线程某个事件已经发生,可以在此事件基础上做你想做的另一个事件了。

二、定义

完成量由struct completion结构体表示
定义于#include<linux/complete.h>

struct completion {
unsigned int done;
wait_queue_head_t wait;
};
  1. done 成员
    done成员用来维护一个计数。当初始化一个完成量时,done成员被初始化为1.由done的类型可以知道这是一个无符号类型,其值永远大于等于0.当done等于0时,会将拥有完成量的线程置于等待状态;当done的值大于0时,表示等待完成量的函数可以立刻执行,而不需要等待。

  2. wait成员
    wait是一个等待队列的链表头,这个链表将所有等待该完成量的进程组成一个链表结构。在这个链表中,存放了正在睡眠的进程链表。

三、初始化完成量

一个完成量必须初始化才能被使用,init_completion()函数用来初始化完成量。其定义如下

static inline void init_completion(struct completion *x)
{
	x->done = 0;
	init_waitqueue_head(&x->wait);      //初始化等待队列头
}

还可以使用宏DECLARE_COMPLETION定义和初始化一个完成量,定义如下:

#define DECLARE_COMPLETION(work) \
	struct completion work = COMPLETION_INITIALIZER(work)

#define COMPLETION_INITIALIZER(work) \
	{ 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }

这个宏的功能跟init_completion()函数的实现是一样的,只是定义合初始化一个完成量的简单实现而已

四、等待完成量

当要实现同步时,可以使用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)
{
	might_sleep();

	spin_lock_irq(&x->wait.lock);
	timeout = do_wait_for_common(x, timeout, state);
	spin_unlock_irq(&x->wait.lock);
	return timeout;
}


static inline long __sched
do_wait_for_common(struct completion *x, 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 = schedule_timeout(timeout);
			spin_lock_irq(&x->wait.lock);
		} while (!x->done && timeout);
		__remove_wait_queue(&x->wait, &wait);
		if (!x->done)
			return timeout;
	}
	x->done--;
	return timeout ?: 1;
}

分析函数的实现可以知道,wait_for_completion函数中,如果complete->done的值等于0,那么线程会进入睡眠。如果此时的值大于0,那么wait_for_completion()函数会将complete->done的值减1,然后继续向下执行。前面说过complete->done的值永远大于等于0。

wait_for_completion()函数会执行一个不会被信号中断的等待。如果调用这个函数之后,没有一个线程完成这个完成量,那么执行wait_for_completion()函数的线程会一直等待下去,线程将不可以退出。

五、释放完成量

当需要同步的任务完成后,可以使用下面的两个函数唤醒完成量。当唤醒之后,wait_for_completion()函数之后的代码才能继续执行。这两个函数的定义如下

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);
}
EXPORT_SYMBOL(complete);
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);
}
EXPORT_SYMBOL(complete_all);

前者只唤醒一个等待的进程或者线程,后者将唤醒所有等待的进程或者线程。

complete函数会将complete->done的值加1,然后唤醒complete->wait中的一个线程。

六、完成量使用示例

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值