上节中我们已经掌握了创建大量内核线程的能力,可惜线程之间还缺乏配合。要知道学习ITC(inter thread communication),和学习IPC(inter process communication)一样,不是件简单的事情。本节就暂且解释一种最简单的线程同步手段—completion。
打开include/linux/completion.h,你就会看到completion使用的全部API。这里简单介绍一下。
struct completion{
unsigned int done;
wait_queue_head_t wait;
};
void init_completion(struct completion *x);
void wait_for_completion(struct completion *x);
void wait_for_completion_interruptible(struct completion *x);
void wait_for_completion_killable(struct completion *x);
unsigned long wait_for_completion_timeout(struct completion *x,
unsigned long timeout);
unsigned long wait_for_completion_interruptible_timeout(struct completion *x,
unsigned long timeout);
bool try_wait_for_completion(struct completion *x);
bool completion_done(struct completion *x);
void complete(struct completion *x);
void complete_all(struct completion *x);
首先是struct completion的结构,由一个计数值和一个等待队列组成。我们就大致明白,completion是类似于信号量的东西,用completion.done来表示资源是否可用,获取不到的线程会阻塞在completion.wait的等待队列上,直到其它线程释放completion。这样理解在实现上不错,但我认为completion不是与具体的资源绑定,而是单纯作为一种线程间同步的机制,它在概念上要比信号量清晰得多。以后会逐渐看到,线程间事件的同步大多靠completion,而资源临界区的保护大多靠信号量。所以说,completion是一种线程间的约会。
init_completion初始化completion结构。初此之外,linux当然还有在定义变量时初始化的方法,都在completion.h中。
wait_for_completion等待在completion上。如果加了interruptible,就表示线程等待可被外部发来的信号打断;如果加了killable,就表示线程只可被kill信号打断;如果加了timeout,表示等待超出一定时间会自动结束等待