浅谈对原子锁的理解
對(duì)原子atomic操作的理解
前言
我們知道,當(dāng)我們修改某一個(gè)變量的時(shí)候,在匯編層面看來(lái),至少需要細(xì)分為“讀->改->寫”三個(gè)過(guò)程,也就是說(shuō),他們?cè)L問(wèn)存儲(chǔ)單元兩次,第一次讀原值,第二次寫新值。
假設(shè)這樣一種場(chǎng)景,兩個(gè)cpu同時(shí)對(duì)同一個(gè)存儲(chǔ)器單元做“讀->改->寫”操作的話。首先,兩個(gè)CPU都試圖讀同一個(gè)單元,但是存儲(chǔ)器仲裁器(對(duì)訪問(wèn)RAM芯片的操作進(jìn)行串行化的硬件電路)插手,只允許其中一個(gè)訪問(wèn)而不讓另一個(gè)延遲,則這兩個(gè)讀操作會(huì)被串行化。當(dāng)?shù)谝粋€(gè)讀操作已經(jīng)完成后,另一個(gè)CPU從存儲(chǔ)器單元正好讀到同一個(gè)(舊)值。然而,兩個(gè)CPU都試圖向那個(gè)存儲(chǔ)器單元寫一新值,總線存儲(chǔ)器訪問(wèn)再一次被存儲(chǔ)器總裁器串行化,最終,兩個(gè)寫操作都成功。但是,全局的結(jié)果是不對(duì)的,因?yàn)閮蓚€(gè)CPU寫入同一(新)值。因此,這很容易導(dǎo)致意想不到的結(jié)果。
所以,避免由于“讀->改->寫”指令引起的競(jìng)爭(zhēng)條件的最容易的辦法,就是確保這樣的操作在芯片級(jí)是原子的。任何一個(gè)這樣的操作都必須以單個(gè)指令執(zhí)行,中斷不能中斷,且避免其他的CPU訪問(wèn)同一存儲(chǔ)器單元。這些很小的原子操作可以建立在其他更靈活進(jìn)制的基礎(chǔ)之上以創(chuàng)建臨界區(qū)。
在x86平臺(tái)上,總的來(lái)說(shuō),CPU提供三種獨(dú)立的原子鎖機(jī)制:原子
保證操作、加LOCK指令前綴和緩存一致性協(xié)議。
概念理解
原子(atom)本意是“不能被進(jìn)一步分割的最小粒子”,而原子操作
(atomic operation)意為“不可被中斷的一個(gè)或一系列操作”。對(duì)原子操作的簡(jiǎn)單描述就是:多個(gè)線程執(zhí)行一個(gè)操作時(shí),其中任何一個(gè)線程要么
完全執(zhí)行完此操作,要么沒(méi)有執(zhí)行此操作的任何步驟,那么這個(gè)操作就
是原子的。原子操作是其他內(nèi)核同步方法的基石。
api介紹
atomic_read(v) 返回*V atomic_set(v, i) 把*v置成iatomic_add(i,v) 給*v增加i atomic_add_return(i,v) 把i加到*v,返回*V的新值atomic_sub(i, v) 從*v中減去i atomic_sub_reurn(i,v) 從*v減i,返回*v的新值 atomic_sub_and_test(i,v) 從*v中減去i,如果結(jié)果為0 則返回1;否則,返回0atomic_inc(v) 把1加到*v atomic_dec(v) 從*v減 1 atomic_inc_return(v) 把1加到*v,返回*v新值 atomic_dec_return(v) 從*v減1,返回* V的新值原子位操作 test_bit(nr, addr) 返回*add的第nr位的值 set_bit(nr,addr) 設(shè)置*addr的第nr位 clear_bit(nr,addr) 清*addr的第nr位 change_bit(nr, addr) 轉(zhuǎn)換*addr的第nr位,并返回他的原值2.dpdk提供的原子操作
static inline int rte_atomic16_cmpset(volatile uint16_t *dst, uint16_t exp, uint16_t src); static inline uint16_t rte_atomic16_exchange(volatile uint16_t *dst, uint16_t val); static inline void rte_atomic16_init(rte_atomic16_t *v) static inline int16_t rte_atomic16_read(const rte_atomic16_t *v) static inline void rte_atomic16_set(rte_atomic16_t *v, int16_t new_value) static inline void rte_atomic16_add(rte_atomic16_t *v, int16_t inc) static inline void rte_atomic16_sub(rte_atomic16_t *v, int16_t dec) static inline void rte_atomic16_inc(rte_atomic16_t *v); static inline void rte_atomic16_dec(rte_atomic16_t *v); static inline int16_t rte_atomic16_add_return(rte_atomic16_t *v, int16_t inc) static inline int16_t rte_atomic16_sub_return(rte_atomic16_t *v, int16_t dec) static inline int rte_atomic16_inc_and_test(rte_atomic16_t *v); static inline int rte_atomic16_dec_and_test(rte_atomic16_t *v); static inline int rte_atomic16_test_and_set(rte_atomic16_t *v); static inline void rte_atomic16_clear(rte_atomic16_t *v)dpdk提供了16、 32和64位的原子操作API,主要實(shí)現(xiàn)原理是使用了**LOCK指令+CMPXCHG指令**。
對(duì)于LOCK指令前綴的總線鎖,早期CPU芯片上有一條引線#HLOCK pin,如果匯編語(yǔ)言的程序中在一條指令前面加上前
綴“LOCK”(這個(gè)前綴表示鎖總線),經(jīng)過(guò)匯編以后的機(jī)器代碼就使CPU在執(zhí)行這條指令的時(shí)候把#HLOCK pin的電位拉低,持續(xù)到這條指
令結(jié)束時(shí)放開(kāi),從而把總線鎖住,這樣同一總線上別的CPU就暫時(shí)不能通過(guò)總線訪問(wèn)內(nèi)存了,保證了這條指令在多處理器環(huán)境中的原子性。
隨著處理器的發(fā)展,對(duì)LOCK前綴的實(shí)現(xiàn)也在不斷進(jìn)行著性能改善。最近幾代處理器中已經(jīng)支持新的鎖技術(shù),若當(dāng)前訪問(wèn)的內(nèi)存已經(jīng)被處理器緩存,LOCK#不會(huì)被觸發(fā),會(huì)用鎖緩存的方式代替。這樣處理原子操作的開(kāi)銷就在這些特定場(chǎng)景下進(jìn)一步降低。
CMPXCHG這條指令,它的語(yǔ)義是比較并交換操作數(shù)(CAS,Compare And Set)。而用XCHG類的指令做內(nèi)存操作,處理器會(huì)自動(dòng)地遵循LOCK的語(yǔ)義,可見(jiàn)該指令是一條原子的CAS單指令操作。
源碼如下
static inline int rte_atomic64_cmpset(volatile uint64_t *dst, uint64_t exp, uint64_t src) {uint8_t res;asm volatile(MPLOCKED"cmpxchgq %[src], %[dst];""sete %[res];": [res] "=a" (res), /* output */[dst] "=m" (*dst): [src] "r" (src), /* input */"a" (exp),"m" (*dst): "memory"); /* no-clobber list */return res; }本人從dpdk移植了鎖實(shí)現(xiàn)到自己的github里面 https://github.com/air5005/usg/tree/master/libs/liblock 有興趣的可以參考.
3.linux kernel
內(nèi)核的原子鎖實(shí)現(xiàn)主要在x86結(jié)構(gòu)里面,使用的是lock指令+內(nèi)存屏障原理
提供的api基本一致,都是分為16、32、64位三種api。
總結(jié)
- 上一篇: 数字性格分析测试软件,MBTI性格分析测
- 下一篇: Codeforces 102202D-A