• 这个时代无锁真的就是无锁吗


    这几天在研究无锁编程的一些事情。

    这里是内核kfifo(无锁循环队列,主要用于单一读者与单一写者)代码介绍:http://blog.csdn.net/linyt/article/details/5764312 代码精妙处原文作者已经解释得十分清楚了,然而,作者略过了这三个函数的介绍 :

    smp_rmb()
    smp_wmb()
    smp_mb()

    这几个函数用于同步CPU各个核的cache line。是不是太专业了一点,其实我也不太懂硬件方面的东西,但总归要知道cpu是多核的,每个核有自己的cache,读写内存都先通过cache。然后呢,内存只有一个,核有多个,也就是说,同一份数据在内存只有一份,但却可能同时存在于多个cache line中。

    现在转过头来说另一个事实,所有无锁算法都依赖于一些原子操作,例如compare_and_set, atomic_add, atomic_sub之类的(这些操作和cpu本身提供的原子指令并不一样,原子指令例如cmpxchg只是确保在完成这个操作之前进程不会被抢占)。这些操作可以执行的一个前提是——独占。假如一个变量a同时存在于核1和核2的cache line中,那么当核1想要进行atomic_add的时候必须先独占这个变量a,也就是告诉核2变量a在你的cache line已经无效了,以后想要操作a的时候到我这里来取最新的值。是不是有点像锁呢?恩,是挺像的,这正是本文标题想要表达的意思。(如果变量a不在cache line中呢?那么核1要锁住的将不是cache line,而是内存总线,一个消耗更大的操作)

    然后上面的三个函数,被称为内存屏障,用于cpu各个核的cache line通信,至于实际上通信的过程是怎样的,这篇写得不能再好(墙外):http://sstompkins.wordpress.com/2011/04/12/why-memory-barrier%EF%BC%9F/

    同时,这三个函数只存在于内核源代码中,要在自己的代码中达到同样的效果,可以使用更上层的api,例如gcc 4.2以上的版本都内置提供__sync_synchronize()这类的函数,效果差不多。(或者你自己去抄内核的实现)

    这里来盗窃一下内核的源代码,修改一下变成一个简单的“无锁”循环队列:

    #define QUEUE_SIZE 1024
    typedef unsigned int ElementType;
    
    struct kfifo
    {
        ElementType data[ QUEUE_SIZE ];
        unsigned int in; 
        unsigned int out;
    };
    
    int kfifo_put( struct kfifo *fifo, ElementType element )
    {
        if ( QUEUE_SIZE - fifo->in + fifo->out == 0 )
            return 0;
    
        __sync_synchronize();      //确保取出的out是最新的(它是这么说的,但极度怀疑不需要)
    
        unsigned int index = fifo->in & (QUEUE_SIZE - 1);
        fifo->data[ index ] = element;
    
        __sync_synchronize();      //确保先写入数据再更新in
    
        fifo->in++;
    
        return 1;
    }
    
    int kfifo_get( struct kfifo *fifo, ElementType *element )
    {
        if ( fifo->in - fifo->out == 0 )
            return 0;
        unsigned int index = fifo->out & (QUEUE_SIZE - 1);
    
        __sync_synchronize();       //确保读出的in是最新的(同上)
    
        *element = fifo->data[ index ];
    
        __sync_synchronize();       //确保先读取数据再更新out
    
        fifo->out++;
    
        return 1;
    }

    嘛,差不多了。

  • 相关阅读:
    C/C++分别读取文件的一行
    (转载)C库函数strtok()
    (转载)C++常量折叠和C语言中const常量对比
    ssh
    ubuntu ufw
    uplevel
    ubuntu lucid source.list
    tail
    socket client with proc
    pack forget
  • 原文地址:https://www.cnblogs.com/madao/p/2943467.html
Copyright © 2020-2023  润新知