Kernel Completion机制

    内核提供的Completion机制用于多线程之间的数据同步。类似于信号量,但是比信号量要安全。其工作原理如下:

        假设我们有两个线程(A和B)以及一个共享的数据Buffer。线程A往Buffer中写入数据,线程B从Buffer中读取数据。那么线程B需要等待线程A写入完成之后才能从Buffer中读取数据。

    (1)定义/声明一个completion结构的实例。即,用于挂起各线程的等待队列。分为静态声明和动态初始化:

            struct completion {
                unsigned int done;
                wait_queue_head_t wait;
            };

            静态声明:

            #define COMPLETION_INITIALIZER(work) \
                { 0, __WAIT_QUEUE_HEAD_INITIALIZER((work).wait) }
            #define DECLARE_COMPLETION(work) \
                struct completion work = COMPLETION_INITIALIZER(work)

            动态初始化:

                static inline void init_completion(struct completion *x)
                {
                    x->done = 0;
                    init_waitqueue_head(&x->wait);
                }

    (2)线程B从Buffer中读取数据前,通过 wait_for_completion(struct completion *c) 将自身挂载到 completion 队列上。wait_for_completion最终调用 do_wait_for_common来实现核心等待功能:

static inline long __sched
do_wait_for_common(struct completion *x,
           long (*action)(long), long timeout, int state)
{
    if (!x->done) {        // done=0,表示需要挂载到等待队列
        DECLARE_WAITQUEUE(wait, current);    // 声明wait调度实体,代表当前线程

        __add_wait_queue_tail_exclusive(&x->wait, &wait);    // x->wait是等待队列头,wait代表当前线程,需要加入x->wait等待队列中
        do {
            if (signal_pending_state(state, current)) {
                timeout = -ERESTARTSYS;
                break;
            }
            __set_current_state(state);    // 当前线程置成不可中断状态
            spin_unlock_irq(&x->wait.lock);    // 解自旋锁(上层调用函数已经上锁,锁定completion)
            timeout = action(timeout);    // action()其实是 schedule_timeout(),执行实际的线程切换,当前线程睡眠,CPU调度其他线程执行


            spin_lock_irq(&x->wait.lock);    // 当前线程从睡眠中恢复运行,首先上锁
        } while (!x->done && timeout);    // 如果done=0,并且也未超时,那么继续睡眠
        __remove_wait_queue(&x->wait, &wait);    // 否则,要么已经完成,要门已经超时。两种情况下都需要将wait从 x->wait等待队列移除
        if (!x->done)
            return timeout;
    }
    x->done--;
    return timeout ?: 1;
}

    (3)线程A往Buffer中写入数据后,通过complete(struct completion *c) 唤醒挂载在completion上的等待线程(本例中是线程B):

void complete(struct completion *x)
{
    unsigned long flags;

    spin_lock_irqsave(&x->wait.lock, flags);    // 上自旋锁
    x->done++;    // 初始done=0,完成一次+1
    __wake_up_locked(&x->wait, TASK_NORMAL, 1);    // 唤醒 x->wait 等待队列上的线程
    spin_unlock_irqrestore(&x->wait.lock, flags);    // 解自旋锁
}

    从上面completion的简单使用方式和API来看,completion的机制还是比较简单的,本质上就是一个等待队列。

转载于:https://my.oschina.net/yepanl/blog/3046803

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值