Linux的原子操作以及LOCK首碼

來源:互聯網
上載者:User

關於原子操作

所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,它的最小的執行單位,不可能有比它更小的執行單位,因此這裡的原子實際是使用了物理學裡的物質微粒的概念。

原子操作需要硬體的支援,因此是架構相關的,其API和原子類型的定義都定義在核心源碼樹的include/asm/atomic.h檔案中,它們都使用組合語言實現,因為C語言並不能實現這樣的操作。

原子操作主要用於實現資源計數,很多引用計數(refcnt)就是通過原子操作實現的。原子類型定義如下:

typedef struct { volatile int counter; } atomic_t; 

volatile修飾欄位告訴gcc不要對該類型的資料做最佳化處理,對它的訪問都是對記憶體的訪問,而不是對寄存器的訪問。 

原子操作API包括:  

atomic_read(atomic_t * v);                                 //該函數對原子類型的變數進行原子讀操作,它返回原子類型的變數v的值。  atomic_set(atomic_t * v, int i);                           //該函數設定原子類型的變數v的值為i。   void atomic_add(int i, atomic_t *v);                       //該函數給原子類型的變數v增加值i。  atomic_sub(int i, atomic_t *v);                            //該函數從原子類型的變數v中減去i。  int atomic_sub_and_test(int i, atomic_t *v);               //該函數從原子類型的變數v中減去i,並判斷結果是否為0,如果為0,返回真,否則返回假。  void atomic_inc(atomic_t *v);                              //該函數對原子類型變數v原子地增加1。   void atomic_dec(atomic_t *v);                              //該函數對原子類型的變數v原子地減1。  int atomic_dec_and_test(atomic_t *v);                      //該函數對原子類型的變數v原子地減1,並判斷結果是否為0,如果為0,返回真,否則返回假。  int  atomic_inc_and_test(atomic_t *v);                     //該函數對原子類型的變數v原子地增加1,並判斷結果是否為0,如果為0,返回真,否則返回假。  int atomic_add_negative(int i, atomic_t *v);               //該函數對原子類型的變數v原子地增加I,並判斷結果是否為負數,如果是,返回真,否則返回假。  int atomic_add_return(int i, atomic_t *v);                 //該函數對原子類型的變數v原子地增加i,並且返回指向v的指標。  int atomic_sub_return(int i, atomic_t *v);                 //該函數從原子類型的變數v中減去i,並且返回指向v的指標。  int atomic_inc_return(atomic_t * v);                       //該函數對原子類型的變數v原子地增加1並且返回指向v的指標。  int atomic_dec_return(atomic_t * v);                       //該函數對原子類型的變數v原子地減1並且返回指向v的指標。 

 

原子操作通常用於實現資源的引用計數,在TCP/IP協議棧的IP片段處理中,就使用了引用計數,片段隊列結構struct ipq描述了一個IP片段,欄位refcnt就是引用計數器,它的類型為atomic_t,當建立IP片段時(在函數ip_frag_create中),使用atomic_set函數把它設定為1,當引用該IP片段時,就使用函數atomic_inc把引用計數加1。 

 

 

關於LOCK

LOCK首碼作用於單個指令上,它對中斷沒有任何影響,因為中斷只能在指令之間產生。LOCK首碼的真正作用是保持對系統匯流排的控制,直到整條指令執行完畢。它在一條指令多次訪問記憶體的時候相當有用。

比如一個共用計數器,我們需要對它進行原子遞增操作,需要做如下工作:

1)從記憶體讀取該計數器的值,臨時將其儲存在CPU內部寄存器中。 2)增加讀取到的值。 3)將被修改後的值寫回記憶體。

在x86體繫結構中,這個遞增操作可以在單個指令中完成,因此中斷不會對該遞增操作產生影響。但是該指令有兩次記憶體訪問操作,讀和寫,另外一個CPU或者核(core)可能同時對該計數器進行遞增操作。如果另外一個CPU/核(core)在第1步完成後,第3步完成前讀取該計數器的值,那麼兩個CPU/核(core)都使用被修改之前的計數器值並對其進行遞增操作。這樣就出現了錯誤的情況。

如果在此時使用了LOCK首碼,第一個核在對該計數器進行操作的時候,保持對匯流排的控制權,直到兩次訪問記憶體的操作都完畢。這樣做的結果是,第二個CPU/核(Core)會延遲一段時間,知道第一個CPU完成所有操作為止。

這裡的第二個CPU/核(core)可以是DMA操作,因此在使用DMA操作的時候尤其要小心。如果處理器使用了緩衝,事情可能由於緩衝一致性問題變得更糟糕。同樣,如果使用了虛擬記憶體,也會增加該問題的嚴重程度。因此,在單個處理器的特殊指令前,最好使用lock首碼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.