RM新时代网站-首页

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

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

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

什么指令集支持原子操作

電子工程師 ? 來源:技術(shù)讓夢想更偉大 ? 作者:技術(shù)讓夢想更偉大 ? 2022-08-02 11:48 ? 次閱讀

前言

這個是在面試的時候遇到的問題,當(dāng)時沒有答出來。回到家以后查了查,整理記錄下來。 原問題:什么指令集支持原子操作?其原理是什么?如果考慮到全部的指令集,問題太大了,這里簡化下。以X86和ARM為例。 原子操作是不可分割的操作,在執(zhí)行完畢時它不會被任何事件中斷。在單處理器系統(tǒng)(UniProcessor,簡稱 UP)中,能夠在單條指令中完成的操作都可以認為是原子操作,因為中斷只能發(fā)生在指令與指令之間。 比如,C語言代碼 8186aa7e-0e86-11ed-ba43-dac502259ad0.png ? 如果未經(jīng)優(yōu)化,有可能生成如下匯編: ? 819e654c-0e86-11ed-ba43-dac502259ad0.png ? 這樣在有多個進程執(zhí)行這段代碼時,就有可能產(chǎn)生并發(fā)問題: ? 81aa7b5c-0e86-11ed-ba43-dac502259ad0.png ? 這就會出現(xiàn)問題。 在單處理器中,解決這個問題的方法是,將count++語句翻譯成單指令操作 81bab972-0e86-11ed-ba43-dac502259ad0.png X86指令集支持inc操作,這樣count操作可以在一條指內(nèi)完成。 進程的上下文切換總是在一條指令執(zhí)行之后完成,所以不會出現(xiàn)上述的并發(fā)問題。對于單處理器來說,一條處理器指令就是一個原子操作。 同樣,ARM里的SWP和X86里的XCHG都是對于單處理器來說,是原子操作。 但是,在多處理器系統(tǒng)(Symmetric Multi-Processor,簡稱 SMP)中情況有所不同,由于系統(tǒng)中有多個處理器在獨立的運行,即使在能單條指令中完成的操作也可能受到干擾。因為這個時候并發(fā)的主題不再是進程,而是處理器。

X86架構(gòu)

Intel X86指令集提供了指令前綴lock用于鎖定前端串行總線FSB,保證了指令執(zhí)行時不會收到其他處理器的干擾。 比如: 81c7a588-0e86-11ed-ba43-dac502259ad0.png 使用lock指令前綴之后,處理期間對count內(nèi)存的并發(fā)訪問(Read/Write)被禁止,從而保證了指令的原子性。 如圖所示: 81db363e-0e86-11ed-ba43-dac502259ad0.pngX86LOCK 其原理在Intel開發(fā)手冊有如下說明:

在執(zhí)行伴隨的指令期間使處理器的LOCK#信號有效(將指令變?yōu)樵又噶睿T诙嗵幚砥鳝h(huán)境中,LOCK#信號確保處理器在信號有效時獨占使用任何共享存儲器。 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ù),則可能會生成未定義的操作碼異常(#UD)。如果LOCK前綴與任何不在上述列表中的指令一起使用,也會產(chǎn)生未定義的操作碼異常。無論是否存在LOCK前綴,XCHG指令都始終聲明LOCK#信號。 LOCK前綴通常與BTS指令一起使用,以在共享存儲器環(huán)境中的存儲器位置上執(zhí)行讀取 – 修改 – 寫入操作。 LOCK前綴的完整性不受存儲器字段對齊的影響。內(nèi)存鎖定是針對任意不對齊的字段。

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

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

static__always_inlineintatomic_cmpxchg(atomic_t*v,intold,intnew)
{
returncmpxchg(&v->counter,old,new);
}


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

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

    關(guān)注

    68

    文章

    19259

    瀏覽量

    229649
  • C語言
    +關(guān)注

    關(guān)注

    180

    文章

    7604

    瀏覽量

    136683
  • 指令集
    +關(guān)注

    關(guān)注

    0

    文章

    222

    瀏覽量

    23378

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

文章出處:【微信號:技術(shù)讓夢想更偉大,微信公眾號:技術(shù)讓夢想更偉大】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    X86和ARM中的指令集支持原子操作

    裸機開發(fā)與RTOS開發(fā)一個非常重要的區(qū)別在于多線程之間的消息傳遞和數(shù)據(jù)共享問題,然而在這中間變量的原子操作是一個非常重要的話題,不同的處理器架構(gòu)和編譯選項都可能生成不同的指令,從而影響到變量的
    發(fā)表于 07-06 16:10 ?1904次閱讀
    X86和ARM中的<b class='flag-5'>指令集</b><b class='flag-5'>支持</b><b class='flag-5'>原子</b><b class='flag-5'>操作</b>

    RISC-V指令集概述

    精度浮點擴展)、M(整數(shù)乘除法)、A(原子擴展)、C(壓縮擴展)等擴展指令。例如,在RV64I基礎(chǔ)上,添加原子、整數(shù)乘除法、雙精度浮點、壓縮指令,則該
    發(fā)表于 11-30 23:30

    Hexagon DSP的指令集

    Hexagon處理器指令集被劃分成了特定的指令類。類的不同決定了指令可以被如何以并行方式結(jié)合。指令類與指令的類型相符合。例如ALU32類包含
    發(fā)表于 09-19 18:13

    請問有STM32 SDIO指令集

    最近在做SD卡,但不是用原子哥的SPI模式,用SDIO模式,但不知道SDIO指令集,數(shù)據(jù)手冊上只有寥寥幾個指令,網(wǎng)上查了許久沒有結(jié)果。望哪位兄弟給一份指令集。qq 741060785謝
    發(fā)表于 02-19 21:25

    簡單介紹ARM的指令集

    處理器架構(gòu)是處理器廠商為同一個系列的處理器規(guī)定的一個規(guī)范。ARM架構(gòu)是一種精簡指令集(RISC)架構(gòu),具有以下RISC架構(gòu)特點:較大的通用寄存器堆。load/store體系結(jié)構(gòu),其中數(shù)據(jù)處理操作僅對
    發(fā)表于 08-18 10:58

    微處理器指令集設(shè)計

    微處理器指令集設(shè)計垂直指令格式指令類型及其使用頻度CISC指令集特點 RISC指令集特點指令集設(shè)
    發(fā)表于 10-29 17:13 ?64次下載
    微處理器<b class='flag-5'>指令集</b>設(shè)計

    SAM88RCRI 指令集

    SAM88RCRI 指令集支持寄存器卷操作,它可完成8 位算術(shù)操作和邏輯操作,共有41條指令集
    發(fā)表于 11-27 11:06 ?33次下載

    ARM指令集詳解

    ARM指令集詳解 內(nèi)容提要 ARM指令集 ARM指令集分類與指令格式 ARM指令的尋址方式 ARM
    發(fā)表于 03-09 09:39 ?263次下載
    ARM<b class='flag-5'>指令集</b>詳解

    8086匯編指令集

    8086匯編指令集 數(shù)據(jù)傳送指令集MOV功能: 把源操作數(shù)送給目的操作數(shù)語法: MOV 目的操作數(shù),源
    發(fā)表于 12-25 09:42 ?4555次閱讀

    sse指令集

    sse指令集 SSE(Streaming SIMD Extensions,單指令多數(shù)據(jù)流擴展)指令集是Intel在Pentium III處理器中率先推出的。其實,早在PIII正式推出之前
    發(fā)表于 12-25 10:59 ?1560次閱讀

    thumb指令集是什么_thumb指令集與arm指令集的區(qū)別

    。thumb不是一個完整的體系結(jié)構(gòu),不能指望處理器只執(zhí)行thumb指令集而不支持arm指令集。 thumb指令集分為:分支指令、數(shù)據(jù)傳送
    發(fā)表于 11-03 17:34 ?1.8w次閱讀
    thumb<b class='flag-5'>指令集</b>是什么_thumb<b class='flag-5'>指令集</b>與arm<b class='flag-5'>指令集</b>的區(qū)別

    mips指令集指的是什么

    指令集是存儲在CPU內(nèi)部,對CPU運算進行指導(dǎo)和優(yōu)化的硬程序。擁有這些指令集,CPU就可以更高效地運行。MIPS指令集屬于精簡指令集,MIPS的所有
    發(fā)表于 12-16 10:25 ?1.3w次閱讀

    risc指令集是什么_有哪些

     RISC指令集是高性能CPU的發(fā)展方向。它與傳統(tǒng)的CISC(復(fù)雜指令集)相對。相比而言,RISC的指令格式統(tǒng)一,種類比較少,尋址方式也比復(fù)雜指令集少。當(dāng)然處理速度就提高很多了。目前在
    發(fā)表于 12-19 11:55 ?2w次閱讀
    risc<b class='flag-5'>指令集</b>是什么_有哪些

    ARM架構(gòu)及ARM指令集 Thumb指令集你了解多少?

    ARM架構(gòu)及ARM指令集、Thumb指令集你了解多少?
    的頭像 發(fā)表于 02-26 16:09 ?7129次閱讀

    精簡指令集的特點_精簡指令集有哪些指令

    精簡指令集計算機RISC的特點是指令及其格式精少,操作和控制簡捷。具體有下列幾個方面。
    的頭像 發(fā)表于 08-10 11:26 ?1.4w次閱讀
    RM新时代网站-首页