博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Kernel Completion机制
阅读量:6035 次
发布时间:2019-06-20

本文共 2009 字,大约阅读时间需要 6 分钟。

hot3.png

    内核提供的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

你可能感兴趣的文章
《服务器SSH Public Key认证指南》-补充
查看>>
我的友情链接
查看>>
Java break continue return 的区别
查看>>
算法(Algorithms)第4版 练习 1.3.4
查看>>
jquery easyUI checkbox复选项获取并传后台
查看>>
浅析NopCommerce的多语言方案
查看>>
设计模式之简单工厂模式
查看>>
C++中变量的持续性、链接性和作用域详解
查看>>
2017 4月5日上午
查看>>
Google Chrome开发者工具
查看>>
第一阶段冲刺报告(一)
查看>>
使用crontab调度任务
查看>>
【转载】SQL经验小记
查看>>
zookeeper集群搭建 docker+zk集群搭建
查看>>
Vue2.5笔记:Vue的实例与生命周期
查看>>
论JVM爆炸的几种姿势及自救方法
查看>>
联合体、结构体简析
查看>>
使用throw让服务器端与客户端进行数据交互[Java]
查看>>
java反射与代理
查看>>
深度分析Java的ClassLoader机制(源码级别)
查看>>