Linux下的原子操作


linux支持的哪些操作是具有原子特性的?知道這些東西是理解和設計無鎖化編程算法的基礎。

__sync_fetch_and_add系列的命令,發現這個系列命令講的最好的一篇文章,英文好的同學可以直接去看原文。Multithreaded simple data type access and atomic variables
__sync_fetch_and_add系列一共有十二個函數,有加/減/與/或/異或/等函數的原子性操作函數,__sync_fetch_and_add,顧名思義,先fetch,然后自加,返回的是自加以前的值。以count = 4為例,調用__sync_fetch_and_add(&count,1),之后,返回值是4,然后,count變成了5.
有__sync_fetch_and_add,自然也就有__sync_add_and_fetch,呵呵這個的意思就很清楚了,先自加,在返回。他們哥倆的關系與i++和++i的關系是一樣的。被譚浩強他老人家收過保護費的都會清楚了。
有了這個寶貝函數,我們就有新的解決辦法了。對於多線程對全局變量進行自加,我們就再也不用理線程鎖了。下面這行代碼,和上面被pthread_mutex保護的那行代碼作用是一樣的,而且也是線程安全的。
__sync_fetch_and_add( &global_int, 1 );
下面是這群函數的全家福,大家看名字就知道是這些函數是干啥的了。
在用gcc編譯的時候要加上選項 -march=i686
// sam:在我的服務器上,發現不加都可以。

type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);

// sam:很納悶為什么后邊要寫省略號,是不是還有不需要我們關心的參數?用的時候不需要傳參數?下面這兩個函數正是哥想要的啦,可以輕松實現互斥鎖的功能。
bool __sync_bool_compare_and_swap (type*ptr, type oldval, type newval, ...)
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)
這兩個函數提供原子的比較和交換,如果*ptr == oldval,就將newval寫入*ptr,
第一個函數在相等並寫入的情況下返回true.
第二個函數在返回操作之前的值。

__sync_synchronize (...)


還有兩個函數:
type __sync_lock_test_and_set (type *ptr, type value, ...)
將*ptr設為value並返回*ptr操作之前的值。

void __sync_lock_release (type *ptr, ...)
將*ptr置0

 

__sync_synchronize (...)


發出一個full barrier.

關於memory barrier,cpu會對我們的指令進行排序,一般說來會提高程序的效率,但有時候可能造成我們不希望得到的結果,舉一個例子,比如我們有一個硬件設備,它有4個寄存器,當你發出一個操作指令的時候,一個寄存器存的是你的操作指令(比如READ),兩個寄存器存的是參數(比如是地址和size),最后一個寄存器是控制寄存器,在所有的參數都設置好之后向其發出指令,設備開始讀取參數,執行命令,程序可能如下:

    write1(dev.register_size,size);
    write1(dev.register_addr,addr);
    write1(dev.register_cmd,READ);
    write1(dev.register_control,GO);


如果最后一條write1被換到了前幾條語句之前,那么肯定不是我們所期望的,這時候我們可以在最后一條語句之前加入一個memory barrier,強制cpu執行完前面的寫入以后再執行最后一條:


    write1(dev.register_size,size);
    write1(dev.register_addr,addr);
    write1(dev.register_cmd,READ);
    __sync_synchronize();
    write1(dev.register_control,GO);


memory barrier有幾種類型:
    acquire barrier : 不允許將barrier之后的內存讀取指令移到barrier之前(linux kernel中的wmb())。
    release barrier : 不允許將barrier之前的內存讀取指令移到barrier之后 (linux kernel中的rmb())。
    full barrier    : 以上兩種barrier的合集(linux kernel中的mb())。


還有兩個函數:

type __sync_lock_test_and_set (type *ptr, type value, ...)
   將*ptr設為value並返回*ptr操作之前的值。

void __sync_lock_release (type *ptr, ...)
     將*ptr置0


示例程序:


#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

static int count = 0;


void *test_func(void *arg)
{
        int i=0;
        for(i=0;i<20000;++i){
                __sync_fetch_and_add(&count,1);
        }
        return NULL;
}

int main(int argc, const char *argv[])
{
        pthread_t id[20];
        int i = 0;

        for(i=0;i<20;++i){
                pthread_create(&id[i],NULL,test_func,NULL);
        }

        for(i=0;i<20;++i){
                pthread_join(id[i],NULL);
        }

        printf("%d\n",count);
        return 0;
}

 

參考原文章:

1、http://blog.csdn.net/hzhsan/article/details/25124901

2、http://www.linuxidc.com/Linux/2011-06/37403.htm


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM