atomic_inc(&v)對變量v用鎖定總線的單指令進行不可分解的"原子"級增量操作,避免v的值由於中斷或多處理器同時操作造成不確定狀態。
原子操作
所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,它的最小的執行單位,不可能有比它更小的執行單位,因此這里的原子實際是使用了物理學里的物質微粒的概念。
原子操作需要硬件的支持,因此是架構相關的,其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。
當不需要引用該IP碎片時,就使用函數ipq_put來釋放該IP碎片,ipq_put使用函數atomic_dec_and_test把引用計數減1並判斷引用計數是否為0,如果是就釋放IP碎片。函數ipq_kill把IP碎片從ipq隊列中刪除,並把該刪除的IP碎片的引用計數減1(通過使用函數atomic_dec實現)。
ARMv6_Architecture.pdf 解釋如下
• LDREX{} , []
This performs a load, then sets a monitor to “watch” the address
• STREX {} , , []
This performs a store and returns “success” in Rd if no intervening access
detected by the monitor.
X86 用LOCK 指令實現
ARM代碼如下, ldrex :
static inline int atomic_add_return(int i, atomic_t *v) { unsigned long tmp; int result; __asm__ __volatile__("@ atomic_add_return/n" "1: ldrex %0, [%2]/n" " add %0, %0, %3/n" " strex %1, %0, [%2]/n" " teq %1, #0/n" " bne 1b" : "=&r" (result), "=&r" (tmp) : "r" (&v->counter), "Ir" (i) : "cc"); return result;