在研究zigbee串口通讯模块的时候看到串口模块的主要结构体有一个变量:
...
struct completion open_done;
...
不明白干什么的,通过阅读代码和网上搜寻资料,整理如下:
///相关结构体
/
completion的结构体如下:
//include\linux\completion.h
struct completion {
unsigned int done; //指示等待事件是否完成,0表示未完成 大于0表示完成 / 多加的变量
wait_queue_head_t wait; //存放等待该事件完成的进程队列
};
等待队列头的定义在include\linux\wait.h里面
struct __wait_queue_head {
spinlock_t lock; //自旋锁变量
struct list_head task_list; //存放等待该事件完成的进程队列
};
/
///初始化
/
在zigbee串口设备open()的时候将这个结构体初始化如下:
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
void init_waitqueue_head(wait_queue_head_t *q)
{
spin_lock_init(&q->lock); //自旋锁初始化为未锁 自旋锁下章介绍
INIT_LIST_HEAD(&q->task_list);//初始化列表
}
/
///等待completion
/
在串口发送数据后就会睡眠,睡眠前会设置timer.
1.如果在timer结束前有消息(串口receive buf收到数据)唤醒当前进程,就停止timer,返回继续操作;
2.如果timer到了还没有消息就会直接调用tmier到期后设置的函数,唤醒本进程。
代码如下:
unsigned long __sched
wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_INTERRUPTIBLE);// completion变量 计时时间 可被中断
}
static long __sched
wait_for_common(struct completion *x, long timeout, int state)/zbdev->opend, 5, 2
{
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)/zbdev->opened, 5, 2
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current); //创建和初始化等待队列,将当前进程加入等待队列,并且加入等待唤醒函数default_wake_funcaton->try_to_wake_up()
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);将上面定义赋值的wait等待节点加入到zgbee得completion链表中
do {
if (state == TASK_INTERRUPTIBLE &&
signal_pending(current)) {//判断当前task是否收到信号,它不处理信号,just check
__remove_wait_queue(&x->wait, &wait);
return -ERESTARTSYS;/等到信号了,就先移除等待队列在退出。
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock); //打开自旋锁 对应上面的上锁
timeout = schedule_timeout(timeout);//这个函数里有任务调度,时间到了就会唤醒这个线程,回来后返回0为过期(设定的时间过了----设置了
//定时器),大于0为没有过期。唤醒后会del timer。
spin_lock_irq(&x->wait.lock); //关闭自旋锁 对应上面的解锁
if (!timeout) {timer到了就直接删除等待队列,然后退出。
__remove_wait_queue(&x->wait, &wait);
return timeout;
}
} while (!x->done);
__remove_wait_queue(&x->wait, &wait);
}
x->done--;
return timeout;
}
/
///completion
/
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags); ///自旋锁关闭 也就是执行下面的函数时,是不允许中断的打扰,和其它线程抢占这个资源的
x->done++;
__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,//不可中断的
1, 1, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags); ///自旋锁打开
preempt_check_resched_delayed(); //内核抢占调度
}
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,/completion->wait, 2|4
int nr_exclusive, int sync, void *key)///1,1,NULL nr_exclusive表示是值唤醒多少个,特定的一个还是所有,取决于这个变量值
{
wait_queue_t *curr, *next;
list_for_each_entry_safe(curr, next, &q->task_list, task_list) {//遍历zgbee里面关于这个completion变量里的等待队列,找到了就执行下面的函数。
unsigned flags = curr->flags;
if (curr->func(curr, mode, sync, key) &&这里调用的就是DECLARE_WAITQUEUE(wait, current);里面注册的try_to_wake_up()函数。
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
curr->func如下:
int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,
void *key)
{
return try_to_wake_up(curr->private, mode | TASK_RUNNING_MUTEX,/上面发送函数注册的thread,mode,
sync, 0);//唤醒等待进程。
}
...
struct completion open_done;
...
不明白干什么的,通过阅读代码和网上搜寻资料,整理如下:
completion 机制的作用:
作用:
虽然信号量可以用于实现同步,但往往可能会出现一些不好的结果。例如:当进程A分配了一个临时信号量变量,把它初始化为关闭的MUTEX,并把其地址传递给进程B,然后在A之上调用down(),进程A打算一旦被唤醒就撤销给信号量。随后,运行在不同CPU上的进程B在同一个信号量上调用up()。然而,up()和down()的目前实现还允许这两个函数在同一个信号量上并发。因此,进程A可以被唤醒并撤销临时信号量,而进程B还在运行up()函数。结果up()可能试图访问一个不存在的数据结构。这样就会出现错误。为了防止发生这种错误就专门设计了completion机制专门用于同步。-------摘自 http://blog.csdn.net/dreamxu/article/details/5866593
也就是说,completion机制是为了完善信号量的不足而产生的,它在信号量的基础上多加了一个变量,让另个一关联的线程在唤醒当前线程前,将多加的一个变量赋值,当前进程在wake up后,还需要检查多加的变量的值,然后才做下面的动作,否则继续等待或报错退出。
///相关结构体
/
completion的结构体如下:
//include\linux\completion.h
struct completion {
unsigned int done; //指示等待事件是否完成,0表示未完成 大于0表示完成 / 多加的变量
wait_queue_head_t wait; //存放等待该事件完成的进程队列
};
等待队列头的定义在include\linux\wait.h里面
struct __wait_queue_head {
spinlock_t lock; //自旋锁变量
struct list_head task_list; //存放等待该事件完成的进程队列
};
/
///初始化
/
在zigbee串口设备open()的时候将这个结构体初始化如下:
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
void init_waitqueue_head(wait_queue_head_t *q)
{
spin_lock_init(&q->lock); //自旋锁初始化为未锁 自旋锁下章介绍
INIT_LIST_HEAD(&q->task_list);//初始化列表
}
/
///等待completion
/
在串口发送数据后就会睡眠,睡眠前会设置timer.
1.如果在timer结束前有消息(串口receive buf收到数据)唤醒当前进程,就停止timer,返回继续操作;
2.如果timer到了还没有消息就会直接调用tmier到期后设置的函数,唤醒本进程。
代码如下:
unsigned long __sched
wait_for_completion_interruptible_timeout(struct completion *x, unsigned long timeout)
{
return wait_for_common(x, timeout, TASK_INTERRUPTIBLE);// completion变量 计时时间 可被中断
}
static long __sched
wait_for_common(struct completion *x, long timeout, int state)/zbdev->opend, 5, 2
{
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)/zbdev->opened, 5, 2
{
if (!x->done) {
DECLARE_WAITQUEUE(wait, current); //创建和初始化等待队列,将当前进程加入等待队列,并且加入等待唤醒函数default_wake_funcaton->try_to_wake_up()
wait.flags |= WQ_FLAG_EXCLUSIVE;
__add_wait_queue_tail(&x->wait, &wait);将上面定义赋值的wait等待节点加入到zgbee得completion链表中
do {
if (state == TASK_INTERRUPTIBLE &&
signal_pending(current)) {//判断当前task是否收到信号,它不处理信号,just check
__remove_wait_queue(&x->wait, &wait);
return -ERESTARTSYS;/等到信号了,就先移除等待队列在退出。
}
__set_current_state(state);
spin_unlock_irq(&x->wait.lock); //打开自旋锁 对应上面的上锁
timeout = schedule_timeout(timeout);//这个函数里有任务调度,时间到了就会唤醒这个线程,回来后返回0为过期(设定的时间过了----设置了
//定时器),大于0为没有过期。唤醒后会del timer。
spin_lock_irq(&x->wait.lock); //关闭自旋锁 对应上面的解锁
if (!timeout) {timer到了就直接删除等待队列,然后退出。
__remove_wait_queue(&x->wait, &wait);
return timeout;
}
} while (!x->done);
__remove_wait_queue(&x->wait, &wait);
}
x->done--;
return timeout;
}
/
///completion
/
void complete(struct completion *x)
{
unsigned long flags;
spin_lock_irqsave(&x->wait.lock, flags); ///自旋锁关闭 也就是执行下面的函数时,是不允许中断的打扰,和其它线程抢占这个资源的
x->done++;
__wake_up_common(&x->wait, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE,//不可中断的
1, 1, NULL);
spin_unlock_irqrestore(&x->wait.lock, flags); ///自旋锁打开
preempt_check_resched_delayed(); //内核抢占调度
}
static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,/completion->wait, 2|4
int nr_exclusive, int sync, void *key)///1,1,NULL nr_exclusive表示是值唤醒多少个,特定的一个还是所有,取决于这个变量值
{
wait_queue_t *curr, *next;
list_for_each_entry_safe(curr, next, &q->task_list, task_list) {//遍历zgbee里面关于这个completion变量里的等待队列,找到了就执行下面的函数。
unsigned flags = curr->flags;
if (curr->func(curr, mode, sync, key) &&这里调用的就是DECLARE_WAITQUEUE(wait, current);里面注册的try_to_wake_up()函数。
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
curr->func如下:
int default_wake_function(wait_queue_t *curr, unsigned mode, int sync,
void *key)
{
return try_to_wake_up(curr->private, mode | TASK_RUNNING_MUTEX,/上面发送函数注册的thread,mode,
sync, 0);//唤醒等待进程。
}