Linux的completion线程同步机制
当Linux一个线程需要对某个硬件设备进行读写访问或者控制操作时,该线程通过系统调用执行的内核读写访问或控制操作函数如果要确保上次操作完成后再进行下一步操作,即等待硬件设备返回上一次操作完成信息(通过中断来获取上次操作完成信息)。注意:我们不能假定中断为该线程的上下文,很大可能性中断和读写/控制操作函数属于不同的线程上下文。
驱动初始化 | 读写/控制操作函数 | 中断处理函数 | |
1 | 定义和初始化completion变量 | ||
2 | 重新启动completion | ||
3 | 读写/控制操作 | ||
4 | 等待completion完成,线程进入休眠队列 | 设备完成读写/控制操作后触发中断 | |
5 | 标记completion完成,唤醒读写/控制操作函数对应的休眠线程 | ||
6 | 从休眠队列中唤醒: 可以重新启动第2步;也可以执行其他代码 |
下面用到的completion相关函数并不完整,但是体现了整个completion的基本使用方法。
1. completion变量的定义和初始化
动态初始化一个completion结构体
struct completion {
unsigned int done;
struct swait_queue_head wait;
};
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_swait_queue_head(&x->wait);
}
2. completion的重新启动
static inline void reinit_completion(struct completion *x)
{
x->done = 0;
}
3. 等待completion完成
下面是一种形式,等待completion完成或者超时,整个等待过程不可以被外部信号中断。
unsigned long __sched
wait_for_completion_timeout(struct completion *x, unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(wait_for_completion_timeout);
4. 标记completion完成
确认完成后唤醒队列上的一个线程。
void complete(struct completion *x)
{
unsigned long flags;
raw_spin_lock_irqsave(&x->wait.lock, flags);
if (x->done != UINT_MAX)
x->done++;
swake_up_locked(&x->wait);
raw_spin_unlock_irqrestore(&x->wait.lock, flags);
}
EXPORT_SYMBOL(complete);