<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > ARM體系架構(gòu)下的同步操作

          ARM體系架構(gòu)下的同步操作

          作者: 時間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
          處理器在訪問共享資源時,必須對臨界區(qū)進(jìn)行同步,即保證同一時間內(nèi),只有一個對臨界區(qū)的訪問者。

          當(dāng)共享資源為一內(nèi)存地址時,原子操作是對該類型共享資源同步訪問的最佳方式。

          本文引用地址:http://www.ex-cimer.com/article/201611/317839.htm

          隨著應(yīng)用的日益復(fù)雜和SMP的廣泛使用,處理器都開始提供硬件同步原語以支持原子地更新內(nèi)存地址。

          CISC處理器比如IA32,可以提供單獨的多種原子指令完成復(fù)雜的原子操作,由處理器保證讀-修改-寫回過程的原子性。

          而RISC則不同,由于除Load和Store的所有操作都必須在寄存器中完成,

          如何保證從裝載內(nèi)存地址到寄存器,到修改寄存器中的值,再到將寄存器中的值寫回內(nèi)存中可以原子性的完成,便成為了處理器設(shè)計的關(guān)鍵。

          ARMv6架構(gòu)開始,ARM處理器提供了Exclusive accesses同步原語,包含兩條指令:

          LDREXSTREX

          LDREX和STREX指令,將對一個內(nèi)存地址的原子操作拆分成兩個步驟,

          同處理器內(nèi)置的記錄exclusive accesses的exclusive monitors一起,完成對內(nèi)存的原子操作。

          LDREX

          LDREX與LDR指令類似,完成將內(nèi)存中的數(shù)據(jù)加載進(jìn)寄存器的操作。

          與LDR指令不同的是,該指令也會同時初始化exclusive monitor來記錄對該地址的同步訪問。例如

          LDREX R1, [R0]

          會將R0寄存器中內(nèi)存地址的數(shù)據(jù),加載進(jìn)R1中并更新exclusive monitor。

          STREX

          該指令的格式為:

          STREX Rd, Rm, [Rn]

          STREX會根據(jù)exclusive monitor的指示決定是否將寄存器中的值寫回內(nèi)存中。

          如果exclusive monitor許可這次寫入,則STREX會將寄存器Rm的值寫回Rn所存儲的內(nèi)存地址中,并將Rd寄存器設(shè)置為0表示操作成功。

          如果exclusive monitor禁止這次寫入,則STREX指令會將Rd寄存器的值設(shè)置為1表示操作失敗并放棄這次寫入。

          應(yīng)用程序可以根據(jù)Rd中的值來判斷寫回是否成功。

          在這篇文章里,首先會以Linux Kernel中ARM架構(gòu)的原子相加操作為例,介紹這兩條指令的使用方法;

          之后,會介紹GCC提供的一些內(nèi)置函數(shù),這些同步函數(shù)使用這兩條指令完成同步操作

          Linux Kernel中的atomic_add函數(shù)

          如下是Linux Kernel中使用的atomic_add函數(shù)的定義,它實現(xiàn)原子的給 v 指向的atomic_t增加 i 的功能。

          1 static inline void atomic_add(int i, atomic_t *v)2 {3         unsigned long tmp;4         int result;5 6         __asm__ __volatile__("@ atomic_addn"7 "1:     ldrex   %0, [%3]n"8 "       add     %0, %0, %4n"9 "       strex   %1, %0, [%3]n"10 "       teq     %1, #0n"11 "       bne     1b"12         : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)13         : "r" (&v->counter), "Ir" (i)14         : "cc");15 }

          在第7行,使用LDREX指令將v->counter所指向的內(nèi)存地址的值裝入寄存器中,并初始化exclusive monitor。

          在第8行,將該寄存器中的值與i相加。

          在第9,10,11行,使用STREX指令嘗試將修改后的值存入原來的地址,

          如果STREX寫入%1寄存器的值為0,則認(rèn)為原子更新成功,函數(shù)返回;

          如果%1寄存器的值不為0,則認(rèn)為exclusive monitor拒絕了本次對內(nèi)存地址的訪問,

          則跳轉(zhuǎn)回第7行重新進(jìn)行以上所述的過程,直到成功將修改后的值寫入內(nèi)存為止。

          該過程可能多次反復(fù)進(jìn)行,但可以保證,在最后一次的讀-修改-寫回的過程中,沒有其他代碼訪問該內(nèi)存地址。

          static inline void atomic_set(atomic_t *v, int i){unsigned long tmp;__asm__ __volatile__("@ atomic_set/n""1:    ldrex    %0, [%1]/n""    strex    %0, %2, [%1]/n""    teq    %0, #0/n""    bne    1b": "=&r" (tmp): "r" (&v->counter), "r" (i): "cc");}

          輸入為v(原子變量),i(要設(shè)置的值),均存放在動態(tài)分配的寄存器中。tmp用來指示操作是否成功。

          GCC內(nèi)置的原子操作函數(shù)

          看了上面的GCC內(nèi)聯(lián)匯編,是不是有點暈?

          在用戶態(tài)下,GCC為我們提供了一系列內(nèi)置函數(shù),這些函數(shù)可以讓我們既享受原子操作的好處,

          又免于編寫復(fù)雜的內(nèi)聯(lián)匯編指令。這一系列的函數(shù)均以__sync開頭,分為如下幾類:

          type __sync_fetch_and_add (type *ptr, type value, ...)type __sync_fetch_and_sub (type *ptr, type value, ...)type __sync_fetch_and_or (type *ptr, type value, ...)type __sync_fetch_and_and (type *ptr, type value, ...)type __sync_fetch_and_xor (type *ptr, type value, ...)type __sync_fetch_and_nand (type *ptr, type value, ...)

          這一系列函數(shù)完成對ptr所指向的內(nèi)存地址的對應(yīng)操作,并返回操作之前的值。

          type __sync_add_and_fetch (type *ptr, type value, ...)type __sync_sub_and_fetch (type *ptr, type value, ...)type __sync_or_and_fetch (type *ptr, type value, ...)type __sync_and_and_fetch (type *ptr, type value, ...)type __sync_xor_and_fetch (type *ptr, type value, ...)type __sync_nand_and_fetch (type *ptr, type value, ...)

          這一系列函數(shù)完成對ptr所指向的內(nèi)存地址的對應(yīng)操作,并返回操作之后的值。

          bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)

          這兩個函數(shù)完成對變量的原子比較和交換。

          即如果ptr所指向的內(nèi)存地址存放的值與oldval相同的話,則將其用newval的值替換。

          返回bool類型的函數(shù)返回比較的結(jié)果,相同為true,不同為false;

          返回type的函數(shù)返回的是ptr指向地址交換前存放的值。

          LDREX 和 STREX

          獨占加載和存儲寄存器。

          語法

          LDREX{cond} Rt, [Rn {, #offset}]STREX{cond} Rd, Rt, [Rn {, #offset}]LDREXB{cond} Rt, [Rn]STREXB{cond} Rd, Rt, [Rn]LDREXH{cond} Rt, [Rn]STREXH{cond} Rd, Rt, [Rn]LDREXD{cond} Rt, Rt2, [Rn]STREXD{cond} Rd, Rt, Rt2, [Rn]

          其中:

          cond

          是一個可選的條件代碼(請參閱條件執(zhí)行)。

          Rd

          是存放返回狀態(tài)的目標(biāo)寄存器。

          Rt

          是要加載或存儲的寄存器。

          Rt2

          為進(jìn)行雙字加載或存儲時要用到的第二個寄存器。

          Rn

          是內(nèi)存地址所基于的寄存器。

          offset

          為應(yīng)用于Rn中的值的可選偏移量。offset只可用于 Thumb-2 指令中。 如果省略offset,則認(rèn)為偏移量為 0。

          LDREX

          LDREX可從內(nèi)存加載數(shù)據(jù)。

          • 如果物理地址有共享 TLB 屬性,則LDREX會將該物理地址標(biāo)記為由當(dāng)前處理器獨占訪問,并且會清除該處理器對其他任何物理地址的任何獨占訪問標(biāo)記。

          • 否則,會標(biāo)記:執(zhí)行處理器已經(jīng)標(biāo)記了一個物理地址,但訪問尚未完畢。

          STREX

          STREX可在一定條件下向內(nèi)存存儲數(shù)據(jù)。 條件具體如下:

          • 如果物理地址沒有共享 TLB 屬性,且執(zhí)行處理器有一個已標(biāo)記但尚未訪問完畢的物理地址,那么將會進(jìn)行存儲,清除該標(biāo)記,并在Rd中返回值 0。

          • 如果物理地址沒有共享 TLB 屬性,且執(zhí)行處理器也沒有已標(biāo)記但尚未訪問完畢的物理地址,那么將不會進(jìn)行存儲,而會在Rd中返回值 1。

          • 如果物理地址有共享 TLB 屬性,且已被標(biāo)記為由執(zhí)行處理器獨占訪問,那么將進(jìn)行存儲,清除該標(biāo)記,并在Rd中返回值 0。

          • 如果物理地址有共享 TLB 屬性,但沒有標(biāo)記為由執(zhí)行處理器獨占訪問,那么不會進(jìn)行存儲,且會在Rd中返回值 1。

          限制

          r15 不可用于Rd、Rt、Rt2或Rn中的任何一個。

          對于STREX,Rd一定不能與Rt、Rt2或Rn為同一寄存器。

          對于 ARM 指令:

          • Rt必須是一個編號為偶數(shù)的寄存器,且不能為 r14

          • Rt2必須為R(t+1)

          • 不允許使用offset。

          對于 Thumb 指令:

          • r13 不可用于Rd、Rt或Rt2中的任何一個

          • 對于LDREXD,Rt和Rt2不可為同一個寄存器

          • offset的值可為 0-1020 范圍內(nèi) 4 的任何倍數(shù)。

          用法

          利用LDREX和STREX可在多個處理器和共享內(nèi)存系統(tǒng)之前實現(xiàn)進(jìn)程間通信。

          出于性能方面的考慮,請將相應(yīng)LDREX指令和STREX指令間的指令數(shù)控制到最少。

          Note

          STREX指令中所用的地址必須要與近期執(zhí)行次數(shù)最多的LDREX指令所用的地址相同。
          如果使用不同的地址,則STREX指令的執(zhí)行結(jié)果將不可預(yù)知。

          體系結(jié)構(gòu)

          ARMLDREX和STREX可用于 ARMv6 及更高版本中。

          ARMLDREXB、LDREXH、LDREXD、STREXB、STREXD和STREXH可用于 ARMv6K 及更高版本中。

          所有這些 32 位 Thumb 指令均可用于 ARMv6T2 及更高版本,但LDREXD和STREXD在 ARMv7-M 架構(gòu)中不可用。

          這些指令均無 16 位版本。

          示例

          MOV r1, #0x1                ; load the ‘lock taken’ valuetryLDREX r0, [LockAddr]        ; load the lock valueCMP r0, #0                  ; is the lock free?STREXEQ r0, r1, [LockAddr]  ; try and claim the lockCMPEQ r0, #0                ; did this succeed?BNE try                     ; no – try again....                        ; yes – we have the lock

          arm/include/asm/atomic.h?v=2.6.33" rel="external nofollow noreferrer" target="_blank">http://lxr.free-electrons.com/source/arch/arm/include/asm/atomic.h?v=2.6.33

          /**  arch/arm/include/asm/atomic.h**  Copyright (C) 1996 Russell King.*  Copyright (C) 2002 Deep Blue Solutions Ltd.** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License version 2 as* published by the Free Software Foundation.*/#ifndef __ASM_ARM_ATOMIC_H#define __ASM_ARM_ATOMIC_H#include #include #include #define ATOMIC_INIT(i)  { (i) }#ifdef __KERNEL__/** On ARM, ordinary assignment (str instruction) doesnt clear the local* strex/ldrex monitor on some implementations. The reason we can use it for* atomic_set() is the clrex or dummy strex done on every exception return.*/#define atomic_read(v)  ((v)->counter)#define atomic_set(v,i) (((v)->counter) = (i))#if __LINUX_ARM_ARCH__ >= 6/** 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.*/static inline void atomic_add(int i, atomic_t *v){unsigned long tmp;int result;__asm__ __volatile__("@ atomic_addn""1:     ldrex   %0, [%2]n""       add     %0, %0, %3n""       strex   %1, %0, [%2]n""       teq     %1, #0n""       bne     1b": "=&r" (result), "=&r" (tmp): "r" (&v->counter), "Ir" (i): "cc");}static inline int atomic_add_return(int i, atomic_t *v){unsigned long tmp;int result;smp_mb();__asm__ __volatile__("@ atomic_add_returnn""1:     ldrex   %0, [%2]n""       add     %0, %0, %3n""       strex   %1, %0, [%2]n""       teq     %1, #0n""       bne     1b": "=&r" (result), "=&r" (tmp): "r" (&v->counter), "Ir" (i): "cc");smp_mb();return result;}static inline void atomic_sub(int i, atomic_t *v){unsigned long tmp;int result;__asm__ __volatile__("@ atomic_subn""1:     ldrex   %0, [%2]n""       sub     %0, %0, %3n""       strex   %1, %0, [%2]n""       teq     %1, #0n""       bne     1b": "=&r" (result), "=&r" (tmp): "r" (&v->counter), "Ir" (i): "cc");}static inline int atomic_sub_return(int i, atomic_t *v){unsigned long tmp;int result;smp_mb();__asm__ __volatile__("@ atomic_sub_returnn""1:     ldrex   %0, [%2]n""       sub     %0, %0, %3n""       strex   %1, %0, [%2]n""       teq     %1, #0n""       bne     1b": "=&r" (result), "=&r" (tmp): "r" (&v->counter), "Ir" (i): "cc");smp_mb();return result;}static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new){unsigned long oldval, res;smp_mb();do {__asm__ __volatile__("@ atomic_cmpxchgn""ldrex  %1, [%2]n""mov    %0, #0n""teq    %1, %3n""strexeq %0, %4, [%2]n": "=&r" (res), "=&r" (oldval): "r" (&ptr->counter), "Ir" (old), "r" (new): "cc");} while (res);smp_mb();return oldval;}static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr){unsigned long tmp, tmp2;__asm__ __volatile__("@ atomic_clear_maskn""1:     ldrex   %0, [%2]n""       bic     %0, %0, %3n""       strex   %1, %0, [%2]n""       teq     %1, #0n""       bne     1b": "=&r" (tmp), "=&r" (tmp2): "r" (addr), "Ir" (mask): "cc");}#else /* ARM_ARCH_6 */#ifdef CONFIG_SMP#error SMP not supported on pre-ARMv6 CPUs#endifstatic inline int atomic_add_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;}#define atomic_add(i, v)        (void) atomic_add_return(i, v)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;}#define atomic_sub(i, v)        (void) atomic_sub_return(i, v)static inline int atomic_cmpxchg(atomic_t *v, int old, int new){int ret;unsigned long flags;raw_local_irq_save(flags);ret = v->counter;if (likely(ret == old))v->counter = new;raw_local_irq_restore(flags);return ret;}static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr){unsigned long flags;raw_local_irq_save(flags);*addr &= ~mask;raw_local_irq_restore(flags);}#endif /* __LINUX_ARM_ARCH__ */#define atomic_xchg(v, new) (xchg(&((v)->counter), new))static inline int atomic_add_unless(atomic_t *v, int a, int u){int c, old;c = atomic_read(v);while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)c = old;return c != u;}#define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)#define atomic_inc(v)           atomic_add(1, v)#define atomic_dec(v)           atomic_sub(1, v)#define atomic_inc_and_test(v)  (atomic_add_return(1, v) == 0)#define atomic_dec_and_test(v)  (atomic_sub_return(1, v) == 0)#define atomic_inc_return(v)    (atomic_add_return(1, v))#define atomic_dec_return(v)    (atomic_sub_return(1, v))#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)#define smp_mb__before_atomic_dec()     smp_mb()#define smp_mb__after_atomic_dec()      smp_mb()#define smp_mb__before_atomic_inc()     smp_mb()#define smp_mb__after_atomic_inc()      smp_mb()#include #endif#endif




          關(guān)鍵詞: ARM體系架構(gòu)同步操

          評論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();