一:概述:

在驱动程序开发中,一种常见的情况是:一个线程需要等待另一个线程执行完某个操咋后,才能继续执行。

前面讲的信号量其实也能够完成这种工作,但其效率比Linux中专门针对这种情况的完成量机制要差些。

 

Linux中提供了一种机制,实现一个线程发送一个信号通知另一个线程开始完成某个任务,这种机制就是完成量。

完成量的目的是告诉一个线程某个事件已经发生,可以在此事件基础上做你想做的另一个事件了。其实完成量和信号量比较类似,但是在这种线程通信的情况下,使用完成量有更高的效率。在内核中,可以通过进程看见使用完成量的代码。完成量是一种轻量级的机制,这种机制在一个线程希望告诉另一个线程某个工作已经完成的情况下是非常有用的。

 

二:完成量的实现

重要结构体:

struct completion {
unsigned int done;
wait_queue_head_t wait;
};

1. done成员

done成员用来维护一个计数。

当初始化一个完成量时, done成员被初始化为0。由done的类型可以知道这是一个无符号类型,其值永远大于等于0。

  1. 当done等于0时,会将拥有完成量的线程置于等待状态;
  2. 当done的值大于0时,表示等待完成量的函数可以立刻执行,而不需等待。

 

2. wait成员

struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t; //定义

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

 

重要函数:

初始化函数:----------------------------------------------------------------------

static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}

也可以:

#define COMPLETION_INITIALIZER_ONSTACK(work) \
({ init_completion(&work); work; })


# define DECLARE_COMPLETION_ONSTACK(work) \
struct completion work = COMPLETION_INITIALIZER_ONSTACK(work)

 

等待完成量:-----------------------------------------------------------------------------------------

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

void __sched wait_for_completion(struct completion *x)
{
wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_UNINTERRUPTIBLE);
}

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;
}

可中断的等待函数:

int __sched wait_for_completion_interruptible(struct completion *x)
{
long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE);
if (t == -ERESTARTSYS)
return t;
return 0;
}

 

可中止的函数:

int __sched wait_for_completion_killable(struct completion *x)
{
long t = wait_for_common(x, MAX_SCHEDULE_TIMEOUT, TASK_KILLABLE);
if (t == -ERESTARTSYS)
return t;
return 0;
}

 

释放完成量:-------------------------------------------------

当需要同步的任务完成后,可以使用下面的两个函数唤醒完成量。

当唤醒之后, 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);
}


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);
}

 

 

 

简单代码:

struct completion com;
int xxx init (void)
{
//...
init_completion (&com);//初始化
//...
}
int xxx A()
{
/*代码A*/
wait_for_completion (&com);//等待B
/*代码B*/
return 0;
}
int xxx B()
{
/*代码C*/
complete(&com);//释放完成量
}

完成量:适用于线程同步