参考:《Linux驱动开发入门与实战》
Linux中提供了一种机制,实现一个线程发送一个信号通知另一个线程已完成某个任务,这种机制就是完成量。完成量的目的是告诉一个线程某事件已经发生,可以在此事件基础上做你想做的另一个事件。
1. 完成量的实现
完成量是实现两个任务之间同步的简单方法,在内核中完成量由struct completion结构体表示。该结构体定义在include\linux\completion.h文件中,其定义如下所示:
struct completion{
unsigned int done;
wait_queue_head_t wait;
};
done成员
done成员维护一个计数。当初始化一个完成量时,done成员被初始化为1。当done等于0时,会将拥有完成量的线程置于等待状态;当done的值大于0时,表示等待完成量的函数可以立刻执行。
wait成员
wait是一个等待队列的链表头,这个链表将所有等待该完成量的进程组成一个链表结构。在这个链表中,存放了正在睡眠的进程链表。
2. 完成量的使用
在Linux中,完成量的类型为struct completion。内核提供了一系列的函数对struct completion进行操作。
2.1 定义和初始化完成量
struct completion com;
一个完成量必须初始化才能被使用,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()函数实现的功能一样,只是定义和初始化一个完成量的简单实现而已。
2.2 等待完成量
当要实现同步时,可以使用wait_for_completion()函数等待一个完成量。代码如下:
void __sched wait_for_completion(struct completion *x);
该函数会执行一个不会被信号中断的等待。如果调用这个函数之后,没有一个线程完成这个完成量,那么执行wait_for_completion()函数的线程会一直等待下去,线程将不可以退出。
2.3 释放完成量
当需要同步的任务完成后,可以使用下面两个函数唤醒完成量。当唤醒之后,wait_for_completion()函数之后的代码才可以继续执行。这两个函数定义如下:
void complete(struct completion *x);
void complete_all(struct completion *x);
2.4 使用完成量
以下是完成量的使用方法:
struct completion com; /* 定义完成量 */
int xxx_init(void)
{
...
init_completion(&com); /* 初始化完成量 */
...
}
int xxx_A()
{
...
/* 代码A */
wait_for_completion(&com); /* 申请完成量 */
/* 代码B */
...
return 0;
}
int xxx_B()
{
...
/* 代码C */
complete(&com); /* 释放完成量 */
...
}
代码中,xxx_init()函数完成了完成量的初始化。在xxx_A()函数中代码会一直执行到wait_for_completion()函数,如果此时com->done的值等于0,那么线程会进入睡眠。如果此时的值大于0,那么wait_for_completion()函数将com->done的值减去1,然后执行代码B部分。
在执行xxx_B()函数的过程中,无论如何代码C都可以顺利执行,complete()函数会将com->done的值加1,然后唤醒com->wait中的一个线程。如果碰巧这个线程是执行xxx_A()函数的线程,那么会将这个线程从com->wait队列中唤醒并执行。