1. 概念
原子操作是指不被打斷的操作,即它是最小的執行單位,最簡單的原子操作就是一條條的匯編指令(不包括一些偽指令,偽指令會被匯編器解釋成多潭訓編指令),在 linux 中原子操作對應的資料結構為 atomic_t,定義如下:
typedef struct {
int counter;
} atomic_t;
本質上就是一個整型變數,之所以定義這么一個資料型別,是為了讓原子操作函式只接受 atomic_t 型別的運算元,如果傳入的不是 atomic_t 型別資料,在程式編譯階段就不會通過;另一個原因就是確保編譯器不會對相應的值進行訪問優化,確保對它的訪問都是對記憶體的訪問,而不是對暫存器的訪問,
2. 賦值操作
ARM 處理器有直接對記憶體地址進行賦值的指令(STR),
#define atomic_set(v,i) (((v)->counter) = (i))
3. 讀操作
用 volatile 來防止編譯器對變數訪問的優化,確保是對記憶體的訪問,而不是對暫存器的訪問,
#define atomic_read(v) (*(volatile int *)&(v)->counter)
4. 加操作
使用獨占指令完成累加操作,
static inline void atomic_add(int i, atomic_t *v)
{
unsigned long tmp;
int result;
// 使用獨占指令讀取,然后執行加操作,獨占寫失敗時就重新執行
__asm__ __volatile__("@ atomic_add\n"
"1: ldrex %0, [%3]\n"
" add %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
: "r" (&v->counter), "Ir" (i)
: "cc");
}
5. 減操作
對比加操作和減操作的代碼可以看出,它們非常的相似,其實不同的地方就一句,所以現在最新的內核原始碼中已經使用宏定義 ATOMIC_OP(op, c_op, asm_op) 來重寫了這部分代碼,
static inline void atomic_sub(int i, atomic_t *v)
{
unsigned long tmp;
int result;
// 使用獨占指令讀取,然后執行減操作,獨占寫失敗時就重新執行
__asm__ __volatile__("@ atomic_sub\n"
"1: ldrex %0, [%3]\n"
" sub %0, %0, %4\n"
" strex %1, %0, [%3]\n"
" teq %1, #0\n"
" bne 1b"
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
: "r" (&v->counter), "Ir" (i)
: "cc");
}
6. 其他操作
類似的原子操作函式還有一些,比如 atomic_XXX_return、atomic_cmpxchg、atomic_clear_mask,以及在此基礎上實作的 atomic_inc、atomic_dec、atomic_XXX_and_test、atomic_XXX_return等,以上代碼都是針對 SMP 處理器的實作方式,針對非 SMP 處理器,由于不存在其他核心的搶占,所以只需要防止其他行程搶占即可實作原子操作,例如加操作:
static inline int atomic_sub_return(int i, atomic_t *v)
{
unsigned long flags;
int val;
// 通過關閉中斷防止其他行程打斷代碼的執行
raw_local_irq_save(flags);
val = v->counter;
v->counter = val -= i;
// 恢復中斷原始的狀態
raw_local_irq_restore(flags);
return val;
}
7. 總結
原子性操作的實作需要具體體系結構相關的指令集的支持,
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/47048.html
標籤:Linux
上一篇:NAS 共享訪問協議 — NFS、SMB、FTP、WebDAV 各有何優勢?
下一篇:C語言實作字串的復制的兩種方法
