linux completion机制的理解(zigbee 串口模块)

在研究zigbee串口通讯模块的时候看到串口模块的主要结构体有一个变量:
...
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);//唤醒等待进程。
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值