新聞中心
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??

?? 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
前言
前面一篇文章中介紹了loongarch架構(gòu)中的基礎(chǔ)部分,包括基礎(chǔ)的整數(shù)運(yùn)算指令、浮點(diǎn)運(yùn)算指令、訪存指令等,以及l(fā)oongarch架構(gòu)中的一些寄存器約定和匯編寫(xiě)法。
這篇文章則主要介紹loongarch架構(gòu)中內(nèi)存一致性模型相關(guān)信息,以及原子指令和柵障指令的含義和使用方法。本文會(huì)先介紹內(nèi)存一致性模型相關(guān)背景知識(shí),然后介紹目前l(fā)oongarch資料中內(nèi)存一致性模型相關(guān)的信息,再介紹loongarch中的原子指令和柵障指令,最后結(jié)合spinlock的實(shí)現(xiàn)說(shuō)明原子指令和柵障指令的使用方法。
一、內(nèi)存一致性模型
內(nèi)存一致性模型(memory consistency model),簡(jiǎn)稱內(nèi)存模型(memory model),是描述程序的與shared memory相關(guān)的訪存指令執(zhí)行順序行為的模型。對(duì)于一段程序,內(nèi)存模型能夠指出哪些訪存指令執(zhí)行順序是cpu允許存在的,哪些執(zhí)行順序是不可能發(fā)生的。
對(duì)于一般應(yīng)用程序的開(kāi)發(fā)人員來(lái)說(shuō),內(nèi)存一致性模型是透明的,代碼看上去是順序執(zhí)行的,在多線程中通過(guò)調(diào)用系統(tǒng)封裝的互斥鎖等就可實(shí)現(xiàn)同步。
但對(duì)于系統(tǒng)開(kāi)發(fā)人員,內(nèi)存一致性模型需要了解,這關(guān)系到如底層匯編中自旋鎖實(shí)現(xiàn)等應(yīng)用場(chǎng)景。
1、SC、TSO和RMO
這里列舉出一些常見(jiàn)的內(nèi)存一致性模型:
- Sequential Consistency (SC):順序一致性模型,不打亂訪存指令順序
- Total Store Order (TSO):一種強(qiáng)序模型。x86的內(nèi)存一致性模型類似于TSO
- Relaxed Memory Order (RMO):弱序模型。如arm中支持一種RMO
其中Load表示加載內(nèi)存操作,Store表示寫(xiě)入內(nèi)存操作。
除了SC模型外,其他幾種都打亂了訪存指令的執(zhí)行順序,這主要是硬件上性能因素的考慮。
下面對(duì)這些模型中定義的訪存指令順序進(jìn)行介紹。
(1)SC中訪存指令順序
對(duì)于Load和Store操作,實(shí)際上總共有4種順序:
- Load->Load
- Load->Store
- Store->Store
- Store->Load
SC模型保證以上所有的順序,即訪存指令實(shí)際的執(zhí)行順序與其在程序中的先后順序相同。
下表為兩個(gè)cpu核并行運(yùn)行的例子:
|
Core C1 |
Core C2 |
說(shuō)明 |
|
S1: x = NEW L1: r1 = y |
S2: y = NEW L2: r2 = x |
x, y初始為0 |
其中,S1和S2為Store操作,L1和L2為L(zhǎng)oad操作,x和y是cpu核C1和C2中共享的變量,r1和r2是cpu核C1和C2中的局部變量。
SC中所有可能的執(zhí)行順序如下:
- S1->L1->S2->L2,結(jié)果(r1, r2) = (0, NEW)
- S2->L2->S1->L1,結(jié)果(r1, r2) = (NEW, 0)
- S1,S2->L1,L2,結(jié)果(r1, r2) = (NEW, NEW)。其中S1和S2之間的順序,以及L1和L2之間的順序不確定
結(jié)果與一般程序員視角中的模型相同。
(2)TSO中訪存指令順序
TSO中保證的訪存指令順序如下:
- Load->Load
- Load->Store
- Store->Store
其中對(duì)于Store->Load可能會(huì)被打亂順序。
下面用與上文SC中相同的例子進(jìn)行說(shuō)明:
|
Core C1 |
Core C2 |
說(shuō)明 |
|
S1: x = NEW L1: r1 = y |
S2: y = NEW L2: r2 = x |
x, y初始為0 |
TSO中所有可能的執(zhí)行順序如下:
- S1->L1->S2->L2,結(jié)果(r1, r2) = (0, NEW)
- S2->L2->S1->L1,結(jié)果(r1, r2) = (NEW, 0)
- S1,S2->L1,L2,結(jié)果(r1, r2) = (NEW, NEW)
- L1,L2->S1,S2,結(jié)果(r1, r2) = (0, 0)
可以看到,TSO中可以出現(xiàn)Load比Store先執(zhí)行的情況,從而導(dǎo)致結(jié)果r1和r2均為0。
(3)RMO中訪存指令順序
在RMO中允許更進(jìn)一步的亂序執(zhí)行,如只保證以下順序:
- Load->Load,且地址相同
- Load->Store,且地址相同
- Store->Store,且地址相同
2、同步方法
由于如TSO、RMO等內(nèi)存模型中訪存指令的執(zhí)行順序不確定,因此硬件上提供了利用原子指令和柵障指令(稱為fence或barrier等)來(lái)進(jìn)行同步。程序中將需要同步的地方手動(dòng)將其用原子指令和柵障指令進(jìn)行標(biāo)記,就能將目標(biāo)代碼同步。
下面用一個(gè)例子進(jìn)行說(shuō)明:
|
Core C1 |
Core C2 |
|
S1: data1 = NEW S2: data2 = NEW F1: FENCE S3: flag = SET |
L1: r1 = flag B1: if (r1 != SET) goto L1 F2: FENCE L2: r2 = data1 L3: r3 = data2 |
其中FENCE指令保證前后的訪存指令順序一致。最后訪存指令的順序?yàn)椋篠1,S2->F1->S3->L1->F2->L2,L3,結(jié)果(r1, r2, r3) = (SET, NEW, NEW)。
如果中間的FENCE指令去掉,那么可能會(huì)出現(xiàn)r2和r3為0的情況。
二、loongarch內(nèi)存模型相關(guān)信息
1、loongarch內(nèi)存一致性模型
根據(jù)loongarch架構(gòu)手冊(cè)中相關(guān)信息,其采用弱一致性(Weakly Consistency)的模型。在該模型下,程序員必須通過(guò)同步指令將對(duì)寫(xiě)共享單元的訪問(wèn)保護(hù)起來(lái),以保證多個(gè)處理器核對(duì)于寫(xiě)共享單元的訪問(wèn)是互斥的。
雖然目前的資料中無(wú)法得知loongarch內(nèi)存一致性模型的具體信息,但可以推測(cè)其應(yīng)該打亂了一些訪存指令的順序,需要借助柵障指令(即fence或barrier指令)進(jìn)行同步。
2、loongarch內(nèi)存訪問(wèn)類型
loongarch架構(gòu)下有三種內(nèi)存訪問(wèn)類型:
- 一致可緩存(Coherent Cached)
- 強(qiáng)序非緩存(Strongly-ordered UnCached)
- 弱序非緩存(Weakly-ordered UnCached)
例如,在頁(yè)表項(xiàng)中的MAT(Memory Access Type)域中可以設(shè)置對(duì)應(yīng)內(nèi)存區(qū)域的訪問(wèn)類型。MAT值與內(nèi)存訪問(wèn)類型的關(guān)系如下:
- 0對(duì)應(yīng)強(qiáng)序非緩存
- 1對(duì)應(yīng)一致可緩存
- 2對(duì)應(yīng)弱序非緩存
- 3為保留
通過(guò)設(shè)置內(nèi)存訪問(wèn)類型,相當(dāng)于規(guī)定了一片內(nèi)存區(qū)域的內(nèi)存一致性模型。loongarch手冊(cè)資料中指出,只有強(qiáng)序非緩存沒(méi)有副作用,即不打亂訪存指令的順序。
不過(guò)loongarch資料中內(nèi)存一致性模型和內(nèi)存訪問(wèn)類型并沒(méi)有詳細(xì)解釋,很多地方存在疑點(diǎn),只能推測(cè)其作用類似于arm上的Normal、Device、Strongly-ordered幾種內(nèi)存模型。
三、loongarch相關(guān)指令
1、柵障指令
loongarch中柵障指令如下:
- dbar指令:dbar hint。dbar指令用于完成load/store訪存操作之間的柵障功能,其中hint用于指示dbar指令的同步對(duì)象和同步程度。hint為0在loongarch中是必須實(shí)現(xiàn)的,如果沒(méi)有專門(mén)的功能實(shí)現(xiàn),其他所有的hint值都將視為hint=0執(zhí)行。hint為0指示一個(gè)完全功能的同步柵障,只有等到之前所有l(wèi)oad/store操作執(zhí)行完后,dbar 0才會(huì)執(zhí)行;且只有dbar 0執(zhí)行后,其后所有的load/store操作才能執(zhí)行。
- ibar指令:ibar hint。ibar指令用于完成單個(gè)處理器核內(nèi)部store操作和取指操作之間的同步,其中hint用于指示dbar指令的同步對(duì)象和同步程度。同樣的,hint為0在loongarch中是必須實(shí)現(xiàn)的。ibar 0確保其后的取指一定能夠觀察到ibar 0指令之前所有store操作的執(zhí)行效果。
2、 原子指令
loongarch中原子指令如下:
- amswap、amadd、ammax等指令:普通原子指令,能夠原子地完成對(duì)某個(gè)內(nèi)存單元的“讀-修改-寫(xiě)”過(guò)程。如amadd.w rd, rk, rj表示將rj寄存器值作為地址,從該地址讀取值寫(xiě)入rd,然后將rd與rk進(jìn)行加法操作,最后將結(jié)果寫(xiě)回該地址,rd中保存該地址中的舊值。整個(gè)過(guò)程是原子的。
- amswap_db、amadd_db、ammax_db等指令:帶數(shù)據(jù)柵障功能的原子指令。除了和上面普通原子指令的功能外,還有dbar指令的效果。
- ll/sc指令:ll/sc一對(duì)指令能夠完成“讀-修改-寫(xiě)”原子操作。
其作用機(jī)制為:ll指令能夠完成讀操作,并同時(shí)記錄訪問(wèn)地址和在相關(guān)寄存器中置上一個(gè)標(biāo)記(LLbit置為1)。sc指令能夠完成寫(xiě)操作,并會(huì)先檢查寄存器中的LLbit,只有當(dāng)LLbit為1時(shí)才進(jìn)行寫(xiě)入。而ll和sc之間的指令完成自定義的修改操作。
在ll/sc指令期間,如果其他處理器核中對(duì)該ll/sc指令操作的相同地址進(jìn)行了寫(xiě)入,那么LLbit就會(huì)被清0,這樣sc指令也會(huì)返回失敗。這樣的硬件檢查機(jī)制保證了原子性。
另外,ll/sc指令也有柵障功能。
具體使用方法參見(jiàn)下文中spinlock實(shí)現(xiàn)。
loongarch原子指令的特點(diǎn)與一些RISC架構(gòu)的原子指令相似,也類似的用ll/sc這樣的指令來(lái)代替了CAS(compare-and-swap)類型原子指令。ll/sc指令相比于CAS類指令,CAS指令實(shí)現(xiàn)鎖時(shí)可能會(huì)有ABA問(wèn)題,而使用ll/sc指令可以避免這個(gè)問(wèn)題。
四、應(yīng)用場(chǎng)景:spinlock
下面通過(guò)spinlock的實(shí)現(xiàn),說(shuō)明原子指令和柵障指令的應(yīng)用。
作為對(duì)比,首先介紹linux3.2中arm下spinlock的實(shí)現(xiàn)。
lock操作函數(shù)如下:
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
// 將lock->lock加載到tmp,ldrex和strex為一對(duì)原子指令
// lock是函數(shù)參數(shù),lock->lock為整型數(shù)據(jù)
"1: ldrex %0, [%1]\n"
// 判斷tmp是否為0
" teq %0, #0\n"
// 失敗則執(zhí)行wfe,等待事件將其喚醒
WFE("ne")
| // 省略了一些實(shí)現(xiàn)細(xì)節(jié)
|-> #define WFE(cond) ALT_SMP("wfe" cond, "nop")
// 成功則將1寫(xiě)入lock->lock,strexeq的結(jié)果保存在tmp
" strexeq %0, %2, [%1]\n"
// 判斷tmp的值,如果strexeq成功,tmp為0,否則為1
" teqeq %0, #0\n"
// 如果strexeq失敗,則跳轉(zhuǎn)到前面標(biāo)簽1,ldrex指令處
" bne 1b"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
// barrier指令
smp_mb();
}
unlock操作函數(shù)如下:
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
// barrier指令
smp_mb();
// 簡(jiǎn)單的將lock->lock置1
__asm__ __volatile__(
" str %1, [%0]\n"
:
: "r" (&lock->lock), "r" (0)
: "cc");
// dsb和sev指令
// dsb為barrier,保證str執(zhí)行完畢后才執(zhí)行sev
// sev發(fā)送事件喚醒正在等待事件的cpu核
dsb_sev();
}
可以看到,arm中也有類似的原子指令和柵障指令。其中l(wèi)drex/strex指令類似于loongarch中的ll/sc指令。并且lock和unlock函數(shù)之后均有柵障指令進(jìn)行同步。
類似地,loongarch中spinlock也可以仿照上述代碼實(shí)現(xiàn):
spin_lock:
// 將lock值加載到寄存器t0,假設(shè)參數(shù)寄存器a0中為傳入的參數(shù)lock
1: ll.d t0, a0, 0
// 如果lock不為0則跳轉(zhuǎn)
bnez t0, 1b
// 將t0置1并寫(xiě)入lock表示上鎖,t0保存sc執(zhí)行結(jié)果
li.d t0, 1
sc.d t0, a0, 0
// 如果sc失敗則跳轉(zhuǎn)
bnez t0, 1b
// barrier
dbar 0
spin_unlock:
dbar 0
// 將參數(shù)lock寫(xiě)入0
st.d zero, a0, 0
總結(jié)
本文介紹了內(nèi)存一致性模型、loongarch架構(gòu)中內(nèi)存一致性模型相關(guān)信息、原子指令和柵障指令的含義和使用方法,并結(jié)合自旋鎖的案例進(jìn)行了說(shuō)明。同時(shí)發(fā)現(xiàn),因?yàn)槟壳笆袌?chǎng)上得到的資料有限,loongarch架構(gòu)內(nèi)存一致性模型中還有一些不清楚的地方,需要后面相關(guān)廠家完善。
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
?? 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
分享名稱:loongarch架構(gòu)介紹—內(nèi)存模型和相關(guān)指令(二)
本文網(wǎng)址:http://www.5511xx.com/article/dppghcc.html


咨詢
建站咨詢
