linux代码之atomic
/*
?* 2020/12/4?? ?15:07?? ?qing
?*
?* 原子操作需要靠硬件實現
?*/
/*
?* atomic
?*/
Linux內核中提供了各種各樣的原子操作函數。除了最原始的讀取和設置之外,還包括各種運算,以及位操作等等。而且有的原子操作還要返回操作過后變量的值,
有的要返回操作之前變量的值,如果再牽涉到內存屏障的問題,將這些因素組合起來,有非常多的原子操作函數。
/*
?* Bus Lock(鎖總線)
?*/
?? ?CPU執行原子指令時,給總線上鎖,這樣在釋放前,可以防止其它CPU的內存操作。
/*
?* Cache Lock
?*/
?? ?除了和IO緊密相關的(如MMIO),大部分的內存都是可以被cache的,由前面介紹的cache一致性原理,我們知道由cacheline處于Exclusive或Modified時,
?? ?該變量只有當前CPU緩存了數據,因此當進行原子操作時,發出Read Invalidate消息,使其它CPU上的緩存無效,cacheline變成Exclusive狀態然后將該
?? ?cacheline上鎖,接著就可以取數據,修改并寫入cacheline,如果這時有其它CPU也進行原子操作,發出read invalidate消息,但由于當前CPU的
?? ?cacheline是locked狀態,因此暫時不會回復消息,這樣其它CPU就一直在等待,直到當前CPU完成,使cacheline變為unlocked狀態。
/*
?* 基本型
?*/
?? ?基本型包括非RMW(Read Modifiy Write)的讀寫操作,以及RMW的算術和位操作等。
?? ?非RMW的操作很簡單,只有兩個,即用來讀取的atomic_read()和用來寫入的atomic_set()。
?? ?一般對單獨變量的讀取或寫入操作本身都是原子的,如果代碼中只對單個變量進行讀寫操作,而從來沒對它使用RMW操作,那一般說明你用錯了,
?? ?沒必要使用內核提供的原子操作。
?? ?算術操作:包括加、減、遞增和遞減,命名形式是atomic_{add,sub,inc,dec}();
?? ?Bit位操作:包括與、或、異或和與非,命名形式是atomic_{and,or,xor,andnot}();
?? ?交換操作:包括交換(atomic_xchg())、比較交換(atomic_cmpxchg())和添加了返回成功與否的比較交換(atomic_try_cmpxchg())。
?? ?除了atomic_t類型外,Linux內核還支持對64位和長整形的atomic64_t和atomic_long_t類型,它們也都有對應的基本原子操作函數,
?? ?只不過函數前綴名分別是atomic64_和atomic_long_
static inline void atomic_add(int i, atomic_t *v)
{
?? ?atomic_add_return(i, v);
}
#ifndef atomic_add_return
#define? atomic_add_return(...)?? ??? ??? ??? ??? ??? ?\
?? ?__atomic_op_fence(atomic_add_return, __VA_ARGS__)
#endif
#ifndef __atomic_op_fence
#define __atomic_op_fence(op, args...)?? ??? ??? ??? ??? ?\
({?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?typeof(op##_relaxed(args)) __ret;?? ??? ??? ??? ?\
?? ?smp_mb__before_atomic();?? ??? ??? ??? ??? ?\
?? ?__ret = op##_relaxed(args);?? ??? ??? ??? ??? ?\
?? ?smp_mb__after_atomic();?? ??? ??? ??? ??? ??? ?\
?? ?__ret;?? ??? ??? ??? ??? ??? ??? ??? ?\
})
#endif
/*
?* 返回修改過后的值
?*/
?? ?如果一個原子操作函數要返回修改過后的原子變量的值,那么該函數名會含有“_return”字符串,并且是在表示具體操作字符串的后面。
?? ?
?? ?例如,atomic_add_return、atomic_dec_return_relaxed等。
/*
?* 要返回修改之前的值
?*/
?? ?如果一個原子操作函數要返回修改之前的原子變量的值,那么該函數名會含有“_fetch”字符串,并且是在表示具體操作字符串的前面。
?? ?
?? ?例如,atomic_fetch_and、atomic_fetch_or_acquire等。
/*
?* 有Acquire和Release單向屏障語義
?*/
?? ?如果一個原子操作函數還需要包括Acquire或者Release單向屏障語義,那么該函數名會有“_acquire”或者“_release”后綴。
?? ?
?? ?例如,atomic_xchg_acquire、atomic_cmpxchg_release等。
?? ?相反的,如果一個原子操作函數名有“_relaxed”后綴,表示這個函數沒有被任何內存屏障保護,可以被任意重排序。
?? ?
?? ?例如,atomic_add_return_relaxed、atomic_xchg_relaxed等。
?? ?只有當一個原子操作函數要返回值的時候才有可能添加_acquire、_release和_relaxed后綴。
/*
?* 原子操作的重排序規則
?*/
?? ?Linux內核的原子操作只保證對單一變量的某個操作是原子的,多個CPU同時操作時,不會出現中間的錯誤狀態。但是,對這個原子操作本身,
?? ?并不一定保證其執行的順序,在SMP系統下,有可能會出現重排序的問題。
?? ?
?? ?因此,前面也提到過,有些原子操作函數自己就帶了一定的內存屏障的語義。具體有沒有帶,帶了多少,可以通過函數名看出來,具體規則如下:
??? 非RMW的原子操作可以被任意重排序;
??? RMW的原子操作,如果沒有返回值可以被任意重排序;
??? RMW的原子操作,如果有返回值,并且沒有_relaxed、_acquire和_release后綴,是有內存屏障保護的,可以保證不被重排序;
??? RMW的原子操作,如果有返回值,且有_relaxed后綴,沒有任何內存屏障保護,可以被任意重排序;
??? RMW的原子操作,如果有返回值,且有_acquire后綴,表示讀(RMW中的R)操作是有Acquire單向屏障保護的;
??? RMW的原子操作,如果有返回值,且有_release后綴,表示寫(RMW中的W)操作是有Release單向屏障保護的;
??? RMW的原子操作,如果有條件判斷,那么條件是否的那部分會被任意重排序。
?? ?對于那些不提供內存屏障語義的原子操作來說,為了保證其本身不被重排序,還需要顯式的在其前面或后面使用內存屏障。
?? ?Linux內核提供了兩個函數,分別是smp_mb__before_atomic()用于原子操作函數的前面,和smp_mb__after_atomic()用于原子操作函數的后面。
/*
?* ARMv8架構下原子操作的實現
?*/
#define ATOMIC_INIT(i)?? ?{ (i) }
?/* atomic_read? */
#define atomic_read(v)?? ??? ??? ?READ_ONCE((v)->counter)
#define __READ_ONCE(x, check)?? ??? ??? ??? ??? ??? ?\
({?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?union { typeof(x) __val; char __c[1]; } __u;?? ??? ??? ?\
?? ?if (check)?? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?__read_once_size(&(x), __u.__c, sizeof(x));?? ??? ?\
?? ?else?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?__read_once_size_nocheck(&(x), __u.__c, sizeof(x));?? ?\
?? ?__u.__val;?? ??? ??? ??? ??? ??? ??? ?\
})
#define READ_ONCE(x) __READ_ONCE(x, 1)
static __always_inline
void __read_once_size(const volatile void *p, void *res, int size)
{
?? ?__READ_ONCE_SIZE;
}
#define __READ_ONCE_SIZE?? ??? ??? ??? ??? ??? ?\
({?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?switch (size) {?? ??? ??? ??? ??? ??? ??? ?\
?? ?case 1: *(__u8 *)res = *(volatile __u8 *)p; break;?? ??? ?\
?? ?case 2: *(__u16 *)res = *(volatile __u16 *)p; break;?? ??? ?\
?? ?case 4: *(__u32 *)res = *(volatile __u32 *)p; break;?? ??? ?\
?? ?case 8: *(__u64 *)res = *(volatile __u64 *)p; break;?? ??? ?\
?? ?default:?? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?barrier();?? ??? ??? ??? ??? ??? ?\
?? ??? ?__builtin_memcpy((void *)res, (const void *)p, size);?? ?\
?? ??? ?barrier();?? ??? ??? ??? ??? ??? ?\
?? ?}?? ??? ??? ??? ??? ??? ??? ??? ?\
})
/* 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();
?? ?}
}
?
/*
?* 廢棄的函數,學習一下C語法 及 指令
?*/
???? static inline int
??? __ll_sc_atomic_add_return_acquire(int i, atomic_t *v)
??? {
?? ??? ?unsigned long tmp;
?? ??? ?int result;
??? ?
?? ??? ?asm volatile(
??? "?????? // 將v->counter預取到CPU緩存\n"?????????????????????????????????? \
??? "?? ?prfm??? pstl1strm, %2\n"?? ??? ??? ??? ??? ?\
??? "?????? // 將v->counter獨占的讀入result中\n"????????????????????????????? \
??? "1:?? ?ldaxr??? %w0, %2\n"?? ??? ??? ??? ??? ???????? \
??? "?????? // result = result + i\n"?????????????????????????????????????? \
??? "?? ?add??? %w0, %w0, %w3\n"?? ??? ??? ??? ???????????????? \
??? "?????? // 將result的值獨占的存入v->counter中\n"?????????????????????????? \
??? "?????? // 如果存入時獨占標記被清除則將tmp置1\n"???????????????????????????? \
??? "?? ?stxr?? ?%w1, %w0, %2\n"?? ??? ??? ??? ??? ???????? \
??? "?????? // 如果tmp被置1則從頭再次執行一遍\n"??????????????????????????????? \
??? "?? ?cbnz?? ?%w1, 1b\n"?? ??? ??? ??? ??? ??? ?\
??????????? )?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)?? ??? ?\
?? ??? ?: __stringify(I) "r" (i)?? ??? ??? ??? ???????? \
?? ??? ?: "memory");?? ??? ??? ??? ??? ???????????????? \
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return result;?? ??? ??? ??? ??? ??? ??? ?\
??? }
?? ?static inline int?? ??? ??? ??? ??? ??? ??? ?\
?? ?__ll_sc_atomic_fetch_or_release(int i, atomic_t *v)?? ??? ??? ?\
?? ?{?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?unsigned long tmp;?? ??? ??? ??? ??? ??? ?\
?? ??? ?int val, result;?? ??? ??? ??? ??? ??? ?\
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?asm volatile(?? ??? ??? ???????????????????????????????? \
?? ?"?????? // 將v->counter預取到CPU緩存\n"?????????????????????????????????? \
?? ?"?? ?prfm?? ?pstl1strm, %3\n"?? ??? ??? ??? ??? ?\
?? ?"?????? // 將v->counter獨占的讀入result中\n"????????????????????????????? \
?? ?"1:?? ?ldxr?? ?%w0, %3\n"?? ??? ??? ??? ??? ???????? \
?? ?"?????? // val = result | i\n"????????????????????????????????????????? \
?? ?"?? ?orr?? ?%w1, %w0, %w4\n"?? ??? ??? ??? ???????? \
?? ?"?????? // 將val的值獨占的存入v->counter中\n"????????????????????????????? \
?? ?"?????? // 如果存入時獨占標記被清除則將tmp置1\n"???????????????????????????? \
?? ?"?? ?stlxr?? ?%w2, %w1, %3\n"?? ??? ??? ??? ??? ???????? \
?? ?"?????? // 如果tmp被置1則從頭再次執行一遍\n"??????????????????????????????? \
?? ?"?? ?cbnz?? ?%w2, 1b\n"?? ??? ??? ??? ??? ??? ?\
?? ??? ?: "=&r" (result), "=&r" (val), "=&r" (tmp), "+Q" (v->counter)?? ?\
?? ??? ?: __stringify(K) "r" (i)?? ??? ??? ??? ???????? \
?? ??? ?: "memory");?? ??? ??? ??? ??? ??? ??? ?\
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return result;?? ??? ??? ??? ??? ??? ??? ?\
?? ?}
/* arch_cmpxchg */
#define __cmpxchg_wrapper(sfx, ptr, o, n)?? ??? ??? ??? ?\
({?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?__typeof__(*(ptr)) __ret;?? ??? ??? ??? ??? ?\
?? ?__ret = (__typeof__(*(ptr)))?? ??? ??? ??? ??? ?\
?? ??? ?__cmpxchg##sfx((ptr), (unsigned long)(o),?? ??? ?\
?? ??? ??? ??? ?(unsigned long)(n), sizeof(*(ptr)));?? ?\
?? ?__ret;?? ??? ??? ??? ??? ??? ??? ??? ?\
})
#define arch_cmpxchg(...)?? ??? ?__cmpxchg_wrapper( _mb, __VA_ARGS__)
arch_cmpxchg最終被宏定義成了__cmpxchg_wrapper,而它又調用了__cmpxchg_mb函數:
#define __CMPXCHG_GEN(sfx)?? ??? ??? ??? ??? ??? ?\
static __always_inline unsigned long __cmpxchg##sfx(volatile void *ptr,?? ?\
?? ??? ??? ??? ??? ??? unsigned long old,?? ??? ?\
?? ??? ??? ??? ??? ??? unsigned long new,?? ??? ?\
?? ??? ??? ??? ??? ??? int size)?? ??? ??? ?\
{?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?switch (size) {?? ??? ??? ??? ??? ??? ??? ?\
?? ?case 1:?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return __cmpxchg_case##sfx##_8(ptr, old, new);?? ??? ?\
?? ?case 2:?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return __cmpxchg_case##sfx##_16(ptr, old, new);?? ??? ?\
?? ?case 4:?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return __cmpxchg_case##sfx##_32(ptr, old, new);?? ??? ?\
?? ?case 8:?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?return __cmpxchg_case##sfx##_64(ptr, old, new);?? ??? ?\
?? ?default:?? ??? ??? ??? ??? ??? ??? ?\
?? ??? ?BUILD_BUG();?? ??? ??? ??? ??? ??? ?\
?? ?}?? ??? ??? ??? ??? ??? ??? ??? ?\
?? ??? ??? ??? ??? ??? ??? ??? ??? ?\
?? ?unreachable();?? ??? ??? ??? ??? ??? ??? ?\
}
?
__CMPXCHG_GEN()
__CMPXCHG_GEN(_acq)
__CMPXCHG_GEN(_rel)
__CMPXCHG_GEN(_mb)
?
#undef __CMPXCHG_GEN
總結
以上是生活随笔為你收集整理的linux代码之atomic的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: rk3399 android 9.0 r
- 下一篇: 如何确定直流电机驱动的 PWM 频率