0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

關(guān)于LDREX和STREX的原理

馬哥Linux運(yùn)維 ? 來(lái)源:wind ? 作者:wind ? 2022-07-01 10:25 ? 次閱讀

前言

這個(gè)是在面試的時(shí)候遇到的問(wèn)題,當(dāng)時(shí)沒(méi)有答出來(lái)?;氐郊乙院蟛榱瞬椋碛涗浵聛?lái)。原問(wèn)題:什么指令集支持原子操作?其原理是什么?如果考慮到全部的指令集,問(wèn)題太大了,這里簡(jiǎn)化下。以X86和ARM為例。原子操作是不可分割的操作,在執(zhí)行完畢時(shí)它不會(huì)被任何事件中斷。在單處理器系統(tǒng)(UniProcessor,簡(jiǎn)稱 UP)中,能夠在單條指令中完成的操作都可以認(rèn)為是原子操作,因?yàn)橹袛嘀荒馨l(fā)生在指令與指令之間。比如,C語(yǔ)言代碼d056f370-f87e-11ec-ba43-dac502259ad0.png ?如果未經(jīng)優(yōu)化,有可能生成如下匯編: d06173ea-f87e-11ec-ba43-dac502259ad0.png ?這樣在有多個(gè)進(jìn)程執(zhí)行這段代碼時(shí),就有可能產(chǎn)生并發(fā)問(wèn)題: d06dcb36-f87e-11ec-ba43-dac502259ad0.png ?這就會(huì)出現(xiàn)問(wèn)題。在單處理器中,解決這個(gè)問(wèn)題的方法是,將count++語(yǔ)句翻譯成單指令操作d07f982a-f87e-11ec-ba43-dac502259ad0.pngX86指令集支持inc操作,這樣count操作可以在一條指內(nèi)完成。進(jìn)程的上下文切換總是在一條指令執(zhí)行之后完成,所以不會(huì)出現(xiàn)上述的并發(fā)問(wèn)題。對(duì)于單處理器來(lái)說(shuō),一條處理器指令就是一個(gè)原子操作。同樣,ARM里的SWP和X86里的XCHG都是對(duì)于單處理器來(lái)說(shuō),是原子操作。但是,在多處理器系統(tǒng)(Symmetric Multi-Processor,簡(jiǎn)稱 SMP)中情況有所不同,由于系統(tǒng)中有多個(gè)處理器在獨(dú)立的運(yùn)行,即使在能單條指令中完成的操作也可能受到干擾。因?yàn)檫@個(gè)時(shí)候并發(fā)的主題不再是進(jìn)程,而是處理器。

X86架構(gòu)

Intel X86指令集提供了指令前綴lock用于鎖定前端串行總線FSB,保證了指令執(zhí)行時(shí)不會(huì)收到其他處理器的干擾。比如:d08c24c8-f87e-11ec-ba43-dac502259ad0.png ?使用lock指令前綴之后,處理期間對(duì)count內(nèi)存的并發(fā)訪問(wèn)(Read/Write)被禁止,從而保證了指令的原子性。如圖所示:d0a07da6-f87e-11ec-ba43-dac502259ad0.pngX86LOCK其原理在Intel開(kāi)發(fā)手冊(cè)有如下說(shuō)明:
DescriptionCauses the processor’s LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal ensures that the processor has exclusive use of any shared memory while the signal is asserted.The LOCK prefix can be prepended only to the following instructions and only to those forms of the instructions where the destination operand is a memory operand: ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, CMPXCHG16B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, and XCHG. If the LOCK prefix is used with one of these instructions and the source operand is a memory operand, an undefined opcode exception (#UD) may be generated. An undefined opcode exception will also be generated if the LOCK prefix is used with any instruction not in the above list. The XCHG instruction always asserts the LOCK# signal regardless of the presence or absence of the LOCK prefix.The LOCK prefix is typically used with the BTS instruction to perform a read-modify-write operation on a memory location in shared memory environment.The integrity of the LOCK prefix is not affected by the alignment of the memory field. Memory locking is observed for arbitrarily misaligned fields.
在執(zhí)行伴隨的指令期間使處理器的LOCK#信號(hào)有效(將指令變?yōu)樵又噶睿?。在多處理器環(huán)境中,LOCK#信號(hào)確保處理器在信號(hào)有效時(shí)獨(dú)占使用任何共享存儲(chǔ)器。LOCK前綴只能附加在下面的指令之前,并且只適用于那些目標(biāo)操作數(shù)是內(nèi)存操作數(shù)的指令格式:ADD,ADC,AND,BTC,BTR,BTS,CMPXCHG,CMPXCH8B,CMPXCHG16B,DEC,INC, NEG,NOT,OR,SBB,SUB,XOR,XADD和XCHG。如果LOCK前綴與這些指令之一一起使用,并且源操作數(shù)是內(nèi)存操作數(shù),則可能會(huì)生成未定義的操作碼異常(#UD)。如果LOCK前綴與任何不在上述列表中的指令一起使用,也會(huì)產(chǎn)生未定義的操作碼異常。無(wú)論是否存在LOCK前綴,XCHG指令都始終聲明LOCK#信號(hào)。LOCK前綴通常與BTS指令一起使用,以在共享存儲(chǔ)器環(huán)境中的存儲(chǔ)器位置上執(zhí)行讀取 – 修改 – 寫(xiě)入操作。LOCK前綴的完整性不受存儲(chǔ)器字段對(duì)齊的影響。內(nèi)存鎖定是針對(duì)任意不對(duì)齊的字段。

操作系統(tǒng)中的實(shí)現(xiàn)

Linux源碼中對(duì)于原子自增一是如下定義的:d0b19668-f87e-11ec-ba43-dac502259ad0.png ?LOCK_PREFIX的定義如下所示:d0c5afc2-f87e-11ec-ba43-dac502259ad0.png ?可見(jiàn):在對(duì)稱多處理器架構(gòu)的情況下,LOCK_PREFIX被解釋為指令前綴lock。而對(duì)于單處理器架構(gòu),LOCK_PREFIX不包含任何內(nèi)容。另外,對(duì)于CAS,有cmpxchg指令進(jìn)行操作。代碼如下:

		static __always_inline int atomic_cmpxchg(atomic_t *v, int old, int new){return cmpxchg(&v->counter, old, new);} #define cmpxchg(ptr, old, new) __cmpxchg(ptr, old, new, sizeof(*(ptr))) #define __cmpxchg(ptr, old, new, size) __raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX) #define __raw_cmpxchg(ptr, old, new, size, lock) ({ __typeof__(*(ptr)) __ret; __typeof__(*(ptr)) __old = (old); __typeof__(*(ptr)) __new = (new); switch (size) { case __X86_CASE_B: { volatile u8 *__ptr = (volatile u8 *)(ptr); asm volatile(lock "cmpxchgb %2,%1" : "=a" (__ret), "+m" (*__ptr) : "q" (__new), "0" (__old) : "memory"); break; } case __X86_CASE_W: { volatile u16 *__ptr = (volatile u16 *)(ptr); asm volatile(lock "cmpxchgw %2,%1" : "=a" (__ret), "+m" (*__ptr) : "r" (__new), "0" (__old) : "memory"); break; } case __X86_CASE_L: { volatile u32 *__ptr = (volatile u32 *)(ptr); asm volatile(lock "cmpxchgl %2,%1" : "=a" (__ret), "+m" (*__ptr) : "r" (__new), "0" (__old) : "memory"); break; } case __X86_CASE_Q: { volatile u64 *__ptr = (volatile u64 *)(ptr); asm volatile(lock "cmpxchgq %2,%1" : "=a" (__ret), "+m" (*__ptr) : "r" (__new), "0" (__old) : "memory"); break; } default: __cmpxchg_wrong_size(); } __ret; })
		

ARM架構(gòu)

在ARM架構(gòu)下,沒(méi)有LOCK#指令,其具體實(shí)現(xiàn)如下:## ARMv6之前 早期的ARM架構(gòu)是不支持SMP的,這些單核架構(gòu)的CPU實(shí)現(xiàn)原子操作的方式就是通過(guò)關(guān)閉CPU中斷來(lái)完成的。在Linux對(duì)于ARM架構(gòu)的代碼下有如下:d0d3319c-f87e-11ec-ba43-dac502259ad0.png ?這個(gè)是好多操作共用的一套代碼。對(duì)于cmpxchg:d0e0a976-f87e-11ec-ba43-dac502259ad0.png ?可以看到,對(duì)v->counter的操作是一個(gè)臨界區(qū),指令的執(zhí)行不能被打斷,內(nèi)存的訪問(wèn)也需要保持沒(méi)有干擾。ARMv6以前的版本通過(guò)關(guān)本地中斷來(lái)保護(hù)這塊臨界區(qū),看起來(lái)相當(dāng)簡(jiǎn)單,其奧秘就在于ARMv6以前的版本不支持SMP。比如經(jīng)典的read-modify-write問(wèn)題,其本質(zhì)是保持一個(gè)對(duì)內(nèi)存read和write訪問(wèn)的原子性問(wèn)題,也就是說(shuō)內(nèi)存的讀和寫(xiě)的訪問(wèn)不能被打斷。對(duì)該問(wèn)題的解決可以通過(guò)硬件、軟件或者軟硬件結(jié)合的方法來(lái)進(jìn)行。早期的ARM CPU給出的方案就是依賴硬件:SWP這個(gè)匯編指令執(zhí)行了一次讀內(nèi)存操作、一次寫(xiě)內(nèi)存操作,但是從程序員的角度看,SWP這條指令就是原子的,讀寫(xiě)之間不會(huì)被任何的異步事件打斷。具體底層的硬件是如何做的呢?這時(shí)候,硬件會(huì)提供一個(gè)lock signal,在進(jìn)行memory操作的時(shí)候設(shè)定lock信號(hào),告訴總線這是一個(gè)不可被中斷的內(nèi)存訪問(wèn),直到完成了SWP需要進(jìn)行的兩次內(nèi)存訪問(wèn)之后再clear lock信號(hào)。多說(shuō)一點(diǎn)關(guān)于SWP和SWPB的內(nèi)容這兩個(gè)指令是用來(lái)同步的,不是用來(lái)執(zhí)行原子操作的。在將獨(dú)占訪問(wèn)引入ARM架構(gòu)之前,SWP和SWPB指令常用于同步。其局限性是:如果中斷在觸發(fā)交換操作時(shí)觸發(fā),則處理器必須在執(zhí)行中斷之前完成指令的加載和存儲(chǔ)部分,從而增加中斷延遲。由于獨(dú)立加載和獨(dú)占存儲(chǔ)是單獨(dú)的指令,因此在使用新的同步基元時(shí)會(huì)降低此效果。但是在多核系統(tǒng)中,交換指令期間阻止所有處理器訪問(wèn)主存會(huì)降低系統(tǒng)性能。在處理器工作在不同頻率但是共享相同主存的多核系統(tǒng)中,情況尤其如此。所以在ARMv6及以后的版本中,棄用了SWP,ARMv6架構(gòu)引入了獨(dú)占訪問(wèn)內(nèi)存為止的概念,提供了更靈活的原子內(nèi)存更新。ARMv6體系結(jié)構(gòu)以Load-Exclusive和Store-Exclusive同步原語(yǔ)LDREX和STREX的形式引入了Load Link和Store Conditional指令。從ARMv6T2開(kāi)始,這些指令在ARM和Thumb指令集中可用。獨(dú)立加載和專有存儲(chǔ)提供了靈活和可擴(kuò)展的同步,取代了棄用的SWP和SWPB指令。后來(lái)使用的是LDREX和STREX指令,在armv7之后就用了ldrex和strex:d0f165cc-f87e-11ec-ba43-dac502259ad0.png ?訪存指令LDREX/STREX和普通的LDR/STR訪存指令不一樣,它是“獨(dú)占”訪存指令。這對(duì)指令訪存過(guò)程由一個(gè)稱作“exclusive monitor”的部件來(lái)監(jiān)視是否可以進(jìn)行獨(dú)占訪問(wèn)。獨(dú)占訪存指令:(1)LDREX R1 ,[R0] 指令是以獨(dú)占的方式從R0所指的地址中取一個(gè)字存放到R0中;(2)STREX R2,R1,[R0] 指令是以獨(dú)占的方式用R1來(lái)更新內(nèi)存,如果獨(dú)占訪問(wèn)條件允許,則更新成功并返回0到R2,否則失敗返回1到R2。

原文標(biāo)題:對(duì)int變量賦值的操作是原子的嗎?為什么?

文章出處:【微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:彭靜

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2903

    瀏覽量

    73543
  • 處理器系統(tǒng)
    +關(guān)注

    關(guān)注

    0

    文章

    8

    瀏覽量

    7781
  • C語(yǔ)言代碼
    +關(guān)注

    關(guān)注

    0

    文章

    10

    瀏覽量

    9132

原文標(biāo)題:對(duì)int變量賦值的操作是原子的嗎?為什么?

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    66ak2h14使用 ldrex過(guò)程中出錯(cuò)

    我使用的開(kāi)發(fā)板是EVMK2H,在使用自旋鎖時(shí)候會(huì)發(fā)現(xiàn)一直處于死鎖狀態(tài),仿真器調(diào)試發(fā)現(xiàn),無(wú)論我內(nèi)存單元中的值是多少,執(zhí)行 LDREX ? R2, [R0] ? ? ? ?;R0為內(nèi)存單元地址 R2讀取到的值始終為0,請(qǐng)問(wèn)在使用LDREX指令之前還需要做什么配置嗎?
    發(fā)表于 06-21 16:55

    stm32的Core_cm3.c文件

    STREX是Cortex用來(lái)實(shí)現(xiàn)互斥訪問(wèn),保護(hù)臨界資源的指令,LDREX執(zhí)行后,只有離它最近的一條存儲(chǔ)指令(STR,STREX)才能執(zhí)行,其他的存儲(chǔ)指令都會(huì)被駁回,而CLREX就是用于清除互斥訪問(wèn)狀態(tài)
    發(fā)表于 07-02 06:19

    STM32F7 MPU設(shè)置跳入硬件錯(cuò)誤中

    最近在使用STM32F746,將內(nèi)部RAM的MPU屬性設(shè)置為MPU_ACCESS_SHAREABLE,發(fā)現(xiàn)如果程序中使用 ldrex 指令,會(huì)跳入硬件錯(cuò)誤中斷,設(shè)置成
    發(fā)表于 01-30 04:07

    ARM平臺(tái)上特有的獨(dú)占訪問(wèn)指令LDREXSTREX該怎樣去使用呢

    來(lái)說(shuō),也在硬件層面上提供了對(duì)LL/SC的支持,LL操作用的是LDREX指令,SC操作用的是STREX指令。本文主要用來(lái)說(shuō)明ARM平臺(tái)上特有的獨(dú)占訪問(wèn)指令LDREXSTREX的工作原理
    發(fā)表于 04-22 09:44

    AHB-lite總線如何處理獨(dú)占訪問(wèn)

    設(shè)計(jì)。處理器從代碼總線正確引導(dǎo),并執(zhí)行指令(已禁用ITCM)。問(wèn)題是我認(rèn)為普通LDR / STR和LDREX / STREX在AXI總線上沒(méi)有區(qū)別。無(wú)論我怎么嘗試,STREX指令都將失?。ǚ祷?
    發(fā)表于 08-18 11:11

    不能將STREX/LDREX與多核共享內(nèi)存訪問(wèn)一起使用嗎?

    我需要確認(rèn)使用具有共享內(nèi)存區(qū)域的 STREX/LDREX 不會(huì)創(chuàng)建全局獨(dú)占訪問(wèn)標(biāo)簽。所以我們不能將 STREX/LDREX 與多核共享內(nèi)存訪問(wèn)一起使用。我認(rèn)為這違反了 cortex-M
    發(fā)表于 04-03 07:45

    關(guān)于各種pon的資料

    關(guān)于各種pon的資料
    發(fā)表于 07-30 15:43 ?0次下載

    關(guān)于DSP-AD的參考文件

    關(guān)于DSP-AD的參考文件
    發(fā)表于 04-12 14:27 ?3次下載
    <b class='flag-5'>關(guān)于</b>DSP-AD的參考文件

    關(guān)于LaunchPad溫度代碼

    關(guān)于LaunchPad溫度代碼。
    發(fā)表于 05-07 17:04 ?7次下載
    <b class='flag-5'>關(guān)于</b>LaunchPad溫度代碼

    關(guān)于STM32的計(jì)數(shù)與延時(shí)

    關(guān)于STM32的計(jì)數(shù)和延時(shí)
    的頭像 發(fā)表于 03-12 11:20 ?6357次閱讀

    淺談鴻蒙內(nèi)核源碼的原子操作

    ARMv6架構(gòu)引入了LDREXSTREX指令,以支持對(duì)共享存儲(chǔ)器更縝密的非阻塞同步。由此實(shí)現(xiàn)的原子操作能確保對(duì)同一數(shù)據(jù)的“讀取-修改-寫(xiě)入”操作在它的執(zhí)行期間不會(huì)被打斷,即操作的原子性。
    的頭像 發(fā)表于 04-25 16:05 ?1208次閱讀
    淺談鴻蒙內(nèi)核源碼的原子操作

    關(guān)于PWM模式的理解

    關(guān)于PWM模式的理解
    發(fā)表于 11-30 14:06 ?3次下載
    <b class='flag-5'>關(guān)于</b>PWM模式的理解

    ARM指令集—SWP指令

    。 SWP指令主要是完畢ARM體系架構(gòu)處理器的同步操作。在Linux操作系統(tǒng)中實(shí)現(xiàn)信號(hào)量的操作??墒谴酥噶钤贏RMv6架構(gòu)后就沒(méi)有採(cǎi)用了,而是通過(guò)擴(kuò)展的LDREXSTREX實(shí)現(xiàn)。本片文章主要介紹SWP的功能...
    發(fā)表于 02-11 15:33 ?6次下載
    ARM指令集—SWP指令

    Vybrid非對(duì)稱多核架構(gòu)的裸機(jī)固件

      另一方面是與運(yùn)行在 Cortex-A5 上的主操作系統(tǒng)進(jìn)行通信的通信基礎(chǔ)設(shè)施。libopencm3 實(shí)現(xiàn)目前不支持通信??赡茏詈?jiǎn)單的通信實(shí)現(xiàn)是定義一個(gè)可以從雙方訪問(wèn)的共享內(nèi)存區(qū)域(考慮使用使用獨(dú)占加載/存儲(chǔ)指令 LDREX/STREX 的同步機(jī)制)。
    的頭像 發(fā)表于 06-14 09:24 ?895次閱讀
    Vybrid非對(duì)稱多核架構(gòu)的裸機(jī)固件

    關(guān)于量子糾纏的故事

    關(guān)于量子糾纏的故事還得從玻爾和愛(ài)因斯坦關(guān)于量子力學(xué)本質(zhì)的爭(zhēng)論開(kāi)始說(shuō)起。
    的頭像 發(fā)表于 11-07 11:22 ?1802次閱讀