1、原子操作思想
原子操作(atomic operation)
,不可分割的操作。其通過(guò)原子變量來(lái)實(shí)現(xiàn),以保證單個(gè)CPU
周期內(nèi),讀寫(xiě)該變量,不能被打斷,進(jìn)而判斷該變量的值,來(lái)解決并發(fā)引起的互斥。
Atomic
類(lèi)型的函數(shù)可以在執(zhí)行期間禁止中斷,并保證在訪問(wèn)變量時(shí)的原子性。
同時(shí),Linux
內(nèi)核提供了兩類(lèi)原子操作的接口,分別是針對(duì)位和整型變量的原子操作。
2、整型變量原子操作
2.1 API接口
對(duì)于整形變量的原子操作,內(nèi)核提供了一系列的
API
接口
/*設(shè)置原子變量的值*/
atomic_t v = ATOMIC_INIT(0); /* 定義原子變量v并初始化為0 */
void atomic_set(atomic_t *v, int i); /* 設(shè)置原子變量的值為i */
/*獲取原子變量的值*/
atomic_read(atomic_t *v); /* 返回原子變量的值*/
/*原子變量的加減*/
void atomic_add(int i, atomic_t *v); /* 原子變量增加i */
void atomic_sub(int i, atomic_t *v); /* 原子變量減少i */
/*原子變量的自增,自減*/
void atomic_inc(atomic_t *v); /* 原子變量增加1 */
void atomic_dec(atomic_t *v); /* 原子變量減少1 */
/*原子變量的操作并測(cè)試*/
int atomic_inc_and_test(atomic_t *v); /*進(jìn)行對(duì)應(yīng)操作后,測(cè)試原子變量值是否為0*/
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
/*原子變量的操作并返回*/
int atomic_add_return(int i, atomic_t *v); /*進(jìn)行對(duì)應(yīng)操作后,返回新的值*/
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
2.2 API實(shí)現(xiàn)
我們下面就介紹幾個(gè)稍微有代表性的接口實(shí)現(xiàn)
以下基于
Linux
內(nèi)核源碼4.19
,剛看是看的時(shí)候,有點(diǎn)摸不著頭腦,因?yàn)槎x的地方和引用的地方較多,不太容易找到,后來(lái)才慢慢得窺門(mén)徑。
2.2.1 原子變量結(jié)構(gòu)體
typedef struct {
int counter;
} atomic_t;
結(jié)構(gòu)體名稱 :atomic_t
文件位置 :include/linux/types.h
主要作用 :原子變量結(jié)構(gòu)體,該結(jié)構(gòu)體只包含一個(gè)整型成員變量counter
,用于存儲(chǔ)原子變量的值。
2.2.2 設(shè)置原子變量操作
2.2.2.1 ATOMIC_INIT
#define ATOMIC_INIT(i) { (i) }
函數(shù)介紹 :定義了一個(gè)ATOMIC類(lèi)型的變量,并初始化為給定的值。
文件位置 :arch/arm/include/asm/atomic.h
,由include/linux/atomic.h
引用
實(shí)現(xiàn)方法 :這個(gè)宏定義比較簡(jiǎn)單,通過(guò)大括號(hào)將值包裹起來(lái)作為一個(gè)結(jié)構(gòu)體,結(jié)構(gòu)體的第一個(gè)成員就用就是給定的該值。
2.2.2.2 atomic_set
#define atomic_set(v,i) WRITE_ONCE(((v)- >counter), (i))
#define WRITE_ONCE(x, val) \\
({ \\
union { typeof(x) __val; char __c[1]; } __u = \\
{ .__val = (__force typeof(x)) (val) }; \\
__write_once_size(&(x), __u.__c, sizeof(x)); \\
__u.__val; \\
})
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
函數(shù)介紹 :該函數(shù)也用作初始化原子變量
文件位置 :由include/linux/atomic.h
引用arch/arm/include/asm/atomic.h
,再引用include/linux/compiler.h
實(shí)現(xiàn)方式 :通過(guò)調(diào)用WRITE_ONCE
來(lái)實(shí)現(xiàn),其中WRITE_ONCE
宏實(shí)現(xiàn)了一些屏蔽編譯器優(yōu)化的技巧,確保寫(xiě)入操作是原子的。
atomic_set
調(diào)用WRITE_ONCE
將i
的值寫(xiě)入原子變量(v)->counter
中,WRITE_ONCE
以保證操作的原子性WRITE_ONCE
用來(lái)保證操作的原子性- 創(chuàng)建
union
聯(lián)合體,包括__val
和__C
成員變量 - 定義一個(gè)
__U
變量,使用強(qiáng)制轉(zhuǎn)換將參數(shù)__val
轉(zhuǎn)換為typeof(x)
類(lèi)型,傳遞給聯(lián)合體變量__u.__val
- 調(diào)用
__write_once_size
函數(shù),將__c
的值寫(xiě)入到x
指向的內(nèi)存地址中。 - 函數(shù)返回
__u.__val。
- 創(chuàng)建
union
聯(lián)合體- 它的特點(diǎn)是存儲(chǔ)多種數(shù)據(jù)類(lèi)型的值,但是所有成員共享同一個(gè)內(nèi)存空間,這樣可以節(jié)省內(nèi)存空間。
- 主要作用是將一個(gè)非字符類(lèi)型的數(shù)據(jù)
x
強(qiáng)制轉(zhuǎn)換為一個(gè)字符類(lèi)型的數(shù)據(jù),以字符類(lèi)型數(shù)據(jù)來(lái)訪問(wèn)該區(qū)塊的內(nèi)存單元。
__write_once_size
函數(shù)實(shí)現(xiàn)了操作的原子性,核心有以下幾點(diǎn):- 該函數(shù)在向內(nèi)存寫(xiě)入數(shù)據(jù)時(shí)使用了
volatile
關(guān)鍵字,告訴編譯器不要進(jìn)行優(yōu)化,每次操作都從內(nèi)存中讀取最新的值。 - 函數(shù)中的
switch
語(yǔ)句保證了對(duì)不同大小的數(shù)據(jù)類(lèi)型使用不同的存儲(chǔ)方式,可以保證內(nèi)存訪問(wèn)的原子性。 - 對(duì)于默認(rèn)情況,則使用了
__builtin_memcpy
函數(shù)進(jìn)行復(fù)制,而這個(gè)函數(shù)具有原子性。 barrier()
函數(shù)指示CPU
要完成所有之前的內(nèi)存操作,以及確保執(zhí)行順序與其他指令不發(fā)生重排。
- 該函數(shù)在向內(nèi)存寫(xiě)入數(shù)據(jù)時(shí)使用了
2.2.3 原子變量的加減
2.2.3.1 ATOMIC_OPS
/*
* ARMv6 UP and SMP safe atomic ops. We use load exclusive and
* store exclusive to ensure that these are atomic. We may loop
* to ensure that the update happens.
*/
#define ATOMIC_OP(op, c_op, asm_op) \\
static inline void atomic_##op(int i, atomic_t *v) \\
{ \\
unsigned long tmp; \\
int result; \\
\\
prefetchw(&v- >counter); \\
__asm__ __volatile__("@ atomic_" #op "\\n" \\
"1: ldrex %0, [%3]\\n" \\
" " #asm_op " %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"); \\
} \\
#define ATOMIC_OP_RETURN(op, c_op, asm_op) \\
static inline int atomic_##op##_return_relaxed(int i, atomic_t *v) \\
{ \\
unsigned long tmp; \\
int result; \\
\\
prefetchw(&v- >counter); \\
\\
__asm__ __volatile__("@ atomic_" #op "_return\\n" \\
"1: ldrex %0, [%3]\\n" \\
" " #asm_op " %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"); \\
\\
return result; \\
}
#define ATOMIC_FETCH_OP(op, c_op, asm_op) \\
static inline int atomic_fetch_##op##_relaxed(int i, atomic_t *v) \\
{ \\
unsigned long tmp; \\
int result, val; \\
\\
prefetchw(&v- >counter); \\
\\
__asm__ __volatile__("@ atomic_fetch_" #op "\\n" \\
"1: ldrex %0, [%4]\\n" \\
" " #asm_op " %1, %0, %5\\n" \\
" strex %2, %1, [%4]\\n" \\
" teq %2, #0\\n" \\
" bne 1b" \\
: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Qo" (v- >counter) \\
: "r" (&v- >counter), "Ir" (i) \\
: "cc"); \\
\\
return result; \\
}
#define ATOMIC_OPS(op, c_op, asm_op) \\
ATOMIC_OP(op, c_op, asm_op) \\
ATOMIC_OP_RETURN(op, c_op, asm_op) \\
ATOMIC_FETCH_OP(op, c_op, asm_op)