91欧美超碰AV自拍|国产成年人性爱视频免费看|亚洲 日韩 欧美一厂二区入|人人看人人爽人人操aV|丝袜美腿视频一区二区在线看|人人操人人爽人人爱|婷婷五月天超碰|97色色欧美亚州A√|另类A√无码精品一级av|欧美特级日韩特级

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

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

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

聊聊原子變量、鎖、內(nèi)存屏障那點(diǎn)事(1)

Linux閱碼場(chǎng) ? 來(lái)源:未知 ? 作者:李倩 ? 2018-08-30 08:20 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

突然想聊聊這個(gè)話題,是因?yàn)橹跎系囊粋€(gè)問(wèn)題多次出現(xiàn)在了我的Timeline里:請(qǐng)問(wèn),多個(gè)線程可以讀一個(gè)變量,只有一個(gè)線程可以對(duì)這個(gè)變量進(jìn)行寫(xiě),到底要不要加鎖?可惜的是很多高票答案語(yǔ)焉不詳,甚至有所錯(cuò)漏。所以我想在這篇文章里斗膽聊聊這個(gè)水挺深的問(wèn)題。受限于個(gè)人水平,文章若有錯(cuò)漏,還望讀者不吝賜教。

首先約定,由于CPU的架構(gòu)和設(shè)計(jì)浩如煙海,本文站在工程師的角度,只談IA32/AMD64(x86-64)架構(gòu),不討論其他架構(gòu)的細(xì)節(jié)和差異。并且文章中主要引用Intel的文檔予以佐證,不關(guān)注AMD在實(shí)現(xiàn)細(xì)節(jié)上的差異。

眾所周知,當(dāng)一個(gè)執(zhí)行中的程序的數(shù)據(jù)被多個(gè)執(zhí)行流并發(fā)訪問(wèn)的時(shí)候,就會(huì)涉及到同步(Synchronization)的問(wèn)題。同步的目的是保證不同執(zhí)行流對(duì)共享數(shù)據(jù)并發(fā)操作的一致性。早在單核時(shí)代,使用鎖或者原子變量就很容易達(dá)成這一目的。甚至因?yàn)镃PU的一些訪存特性,對(duì)某些內(nèi)存對(duì)齊數(shù)據(jù)的讀或?qū)懸簿哂性拥奶匦浴?/p>

比如,在《Intel? 64 and IA-32 Architectures Software Developer’s Manual》的第三卷System Programming Guide的Chapter 8 Multiple-Processor Management里,就給出了這樣的說(shuō)明:

也就是說(shuō),有些內(nèi)存對(duì)齊的數(shù)據(jù)的訪問(wèn)在CPU層面就是原子進(jìn)行的(注意這里說(shuō)的只是單次的讀或者寫(xiě),類似普通變量i的i++操作不止一次內(nèi)存訪問(wèn))。此時(shí),環(huán)形隊(duì)列(Ring buffer)這種數(shù)據(jù)結(jié)構(gòu)在某些架構(gòu)的單核CPU上,只有一個(gè)Reader和一個(gè)Writer的情況下是不需要額外同步措施的。原因就是read_index和writer_index的寫(xiě)操作在滿足對(duì)齊內(nèi)存訪問(wèn)的情況下是原子的,不需要額外的同步措施。注意這里我加粗了單核CPU這個(gè)關(guān)鍵字,那么到了多核心處理器的今天,該操作就不是原子了嗎?不,依舊是原子的,但是出現(xiàn)了其他的干擾因素迫使可能需要額外的同步措施才能保證原本無(wú)鎖代碼的正確運(yùn)行。

首先是現(xiàn)代編譯器的代碼優(yōu)化和編譯器指令重排可能會(huì)影響到代碼的執(zhí)行順序。編譯期指令重排是通過(guò)調(diào)整代碼中的指令順序,在不改變代碼語(yǔ)義的前提下,對(duì)變量訪問(wèn)進(jìn)行優(yōu)化。從而盡可能的減少對(duì)寄存器的讀取和存儲(chǔ),并充分復(fù)用寄存器。但是編譯器對(duì)數(shù)據(jù)的依賴關(guān)系判斷只能在單執(zhí)行流內(nèi),無(wú)法判斷其他執(zhí)行流對(duì)競(jìng)爭(zhēng)數(shù)據(jù)的依賴關(guān)系。就拿無(wú)鎖環(huán)形隊(duì)列來(lái)說(shuō),如果Writer做的是先放置數(shù)據(jù),再更新索引的行為。如果索引先于數(shù)據(jù)更新,Reader就有可能會(huì)因?yàn)榕袛嗨饕迅露x到臟數(shù)據(jù)。

那禁止編譯器對(duì)該類變量的優(yōu)化,解決了編譯期的重排序就沒(méi)事了嗎?不,CPU還有亂序執(zhí)行(Out-of-Order Execution)的特性。流水線(Pipeline)和亂序執(zhí)行是現(xiàn)代CPU基本都具有的特性。機(jī)器指令在流水線中經(jīng)歷取指、譯碼、執(zhí)行、訪存、寫(xiě)回等操作。為了CPU的執(zhí)行效率,流水線都是并行處理的,在不影響語(yǔ)義的情況下。處理器次序(Process Ordering,機(jī)器指令在CPU實(shí)際執(zhí)行時(shí)的順序)和程序次序(Program Ordering,程序代碼的邏輯執(zhí)行順序)是允許不一致的,即滿足As-if-Serial特性。顯然,這里的不影響語(yǔ)義依舊只能是保證指令間的顯式因果關(guān)系,無(wú)法保證隱式因果關(guān)系。即無(wú)法保證語(yǔ)義上不相關(guān)但是在程序邏輯上相關(guān)的操作序列按序執(zhí)行。從此單核時(shí)代CPU的Self-Consistent特性在多核時(shí)代已不存在,多核CPU作為一個(gè)整體看,不再滿足Self-Consistent特性。

簡(jiǎn)單總結(jié)一下,如果不做多余的防護(hù)措施,單核時(shí)代的無(wú)鎖環(huán)形隊(duì)列在多核CPU中,一個(gè)CPU核心上的Writer寫(xiě)入數(shù)據(jù),更新index后。另一個(gè)CPU核心上的Reader依靠這個(gè)index來(lái)判斷數(shù)據(jù)是否寫(xiě)入的方式不一定可靠。index有可能先于數(shù)據(jù)被寫(xiě)入,從而導(dǎo)致Reader讀到臟數(shù)據(jù)。

所有的麻煩到這里就結(jié)束了嗎?當(dāng)然不,還有Cache的問(wèn)題。前文提到的都是順序一致性(Sequential Consistency)的問(wèn)題,沒(méi)有涉及Cache一致性(Cache Coherence)的問(wèn)題。雖然說(shuō)一般情況下程序員只需要關(guān)注順序一致性即可,但是區(qū)分清楚這兩個(gè)概念也能更好的解釋內(nèi)存屏障(Memory Barrier)。

開(kāi)始提到Cache一致性協(xié)議之前,先介紹兩個(gè)名詞:

Load/Read CPU讀操作,是指將內(nèi)存數(shù)據(jù)加載到寄存器的過(guò)程

Store/Write CPU寫(xiě)操作,是指將寄存器數(shù)據(jù)寫(xiě)回主存的過(guò)程

現(xiàn)代處理器的緩存一般分為三級(jí),由每一個(gè)核心獨(dú)享的L1、L2 Cache,以及所有的核心共享L3 Cache組成:

由于Cache的容量很小,一般都是充分的利用局部性原理,按行/塊來(lái)和主存進(jìn)行批量數(shù)據(jù)交換,以提升數(shù)據(jù)的訪問(wèn)效率。以前寫(xiě)過(guò)一篇《淺析x86架構(gòu)中cache的組織結(jié)構(gòu)》,這里不再贅述。既然各個(gè)核心之間有獨(dú)立的Cache存儲(chǔ)器,那么這些存儲(chǔ)器之間的數(shù)據(jù)同步就是個(gè)比較復(fù)雜的事情。緩存數(shù)據(jù)的一致性由緩存一致性協(xié)議保證。這里比較經(jīng)典的當(dāng)屬M(fèi)ESI協(xié)議。Intel的處理器使用從MESI中演化出的MESIF協(xié)議,而AMD使用MOESI協(xié)議。緩存一致性協(xié)議的細(xì)節(jié)超出了本文的討論范圍,有興趣的讀者可以自行研究。

傳統(tǒng)的MESI協(xié)議中有兩個(gè)行為的執(zhí)行成本比較大。一個(gè)是將某個(gè)Cache Line標(biāo)記為Invalid狀態(tài),另一個(gè)是當(dāng)某Cache Line當(dāng)前狀態(tài)為Invalid時(shí)寫(xiě)入新的數(shù)據(jù)。所以CPU通過(guò)Store Buffer和Invalidate Queue組件來(lái)降低這類操作的延時(shí)。如圖:

當(dāng)一個(gè)核心在Invalid狀態(tài)進(jìn)行寫(xiě)入時(shí),首先會(huì)給其它CPU核發(fā)送Invalid消息,然后把當(dāng)前寫(xiě)入的數(shù)據(jù)寫(xiě)入到Store Buffer中。然后異步在某個(gè)時(shí)刻真正的寫(xiě)入到Cache Line中。當(dāng)前CPU核如果要讀Cache Line中的數(shù)據(jù),需要先掃描Store Buffer之后再讀取Cache Line(Store-Buffer Forwarding)。但是此時(shí)其它CPU核是看不到當(dāng)前核的Store Buffer中的數(shù)據(jù)的,要等到Store Buffer中的數(shù)據(jù)被刷到了Cache Line之后才會(huì)觸發(fā)失效操作。而當(dāng)一個(gè)CPU核收到Invalid消息時(shí),會(huì)把消息寫(xiě)入自身的Invalidate Queue中,隨后異步將其設(shè)為Invalid狀態(tài)。和Store Buffer不同的是,當(dāng)前CPU核心使用Cache時(shí)并不掃描Invalidate Queue部分,所以可能會(huì)有極短時(shí)間的臟讀問(wèn)題。當(dāng)然這里的Store Buffer和Invalidate Queue的說(shuō)法是針對(duì)一般的SMP架構(gòu)來(lái)說(shuō)的,不涉及具體架構(gòu)。事實(shí)上除了Store Buffer和Load Buffer,流水線為了實(shí)現(xiàn)并行處理,還有Line Fill Buffer/Write Combining Buffer 等組件,參考文獻(xiàn)8-10給出了相關(guān)的資料可以進(jìn)一步閱讀。

聲明:本文內(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)投訴
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5608

    瀏覽量

    129998
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    11279

    瀏覽量

    225015
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1672

    瀏覽量

    51617

原文標(biāo)題:淺墨: 聊聊原子變量、鎖、內(nèi)存屏障那點(diǎn)事(1)

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    從硬件引申出內(nèi)存屏障,帶你深入了解Linux內(nèi)核RCU

    本文從硬件的角度引申出內(nèi)存屏障,這不是內(nèi)存屏障的詳盡手冊(cè),但是相關(guān)知識(shí)對(duì)于理解RCU有所幫助。
    的頭像 發(fā)表于 09-19 11:39 ?6641次閱讀
    從硬件引申出<b class='flag-5'>內(nèi)存</b><b class='flag-5'>屏障</b>,帶你深入了解Linux內(nèi)核RCU

    ARM體系結(jié)構(gòu)之內(nèi)存序與內(nèi)存屏障

    本文介紹 Armv8-A 架構(gòu)的內(nèi)存序模型,并介紹 arm 的各種內(nèi)存屏障。本文還會(huì)指出一些需要明確內(nèi)存保序的場(chǎng)景,并指明如何使用內(nèi)存
    發(fā)表于 06-15 18:19 ?2890次閱讀
    ARM體系結(jié)構(gòu)之<b class='flag-5'>內(nèi)存</b>序與<b class='flag-5'>內(nèi)存</b><b class='flag-5'>屏障</b>

    詳解Linux內(nèi)核原子操作

    原子操作(atomic operation),不可分割的操作。其通過(guò)原子變量來(lái)實(shí)現(xiàn),以保證單個(gè)CPU周期內(nèi),讀寫(xiě)該變量,不能被打斷,進(jìn)而判斷該變量
    發(fā)表于 07-04 11:16 ?1226次閱讀

    編程中的命名設(shè)計(jì)那點(diǎn)

    編程中的命名設(shè)計(jì)那點(diǎn)
    發(fā)表于 08-17 09:32

    MCU上的無(wú)原子讀操作

    32位變量任何一個(gè)字節(jié)的時(shí)候,剩下的7個(gè)字節(jié)都可能改變。2、認(rèn)為在中斷函數(shù)建立數(shù)據(jù)拷貝這個(gè)理由同上,無(wú)論如何復(fù)制,都難以避免讀的瞬間數(shù)據(jù)被破壞3、建立單字節(jié)原子該體系必須支持測(cè)試清零指令,而且就算支持
    發(fā)表于 03-06 09:39

    CPU和內(nèi)存那點(diǎn)事兒

    我們之前講過(guò)CPU,也說(shuō)了CPU和內(nèi)存那點(diǎn)事兒,今天咱就再來(lái)說(shuō)說(shuō)有關(guān)內(nèi)存,作為一個(gè)程序員,你必須要懂的哪那些硬核知識(shí)!大白話聊一聊,很重要!先來(lái)大白話的跟大家聊一聊,我們這里說(shuō)的內(nèi)存
    發(fā)表于 07-27 08:02

    導(dǎo)致ARM內(nèi)存屏障的原因究竟有哪些

    與程序員的代碼邏輯不符,導(dǎo)致一些錯(cuò)誤的發(fā)生,為了保證內(nèi)存訪問(wèn)的一致性,也是保證程序的正確性,使用內(nèi)存屏障來(lái)保證內(nèi)存的訪問(wèn)順序。ARM采用的是弱一致性
    發(fā)表于 05-09 09:32

    學(xué)習(xí)下ARM內(nèi)存屏障(memory barrier)指令

    據(jù)標(biāo)記放在普通型內(nèi)存中。如果需要嚴(yán)格的內(nèi)存訪問(wèn)順序,即在需要強(qiáng)制排序的情況下,可以通過(guò)使用顯式屏障操作來(lái)實(shí)現(xiàn)。處理器必須始終負(fù)責(zé)由地址依賴性引起的危險(xiǎn):STR X0, [X2]LDR X1
    發(fā)表于 02-07 14:08

    內(nèi)存屏障是什么

    內(nèi)存屏障,也稱內(nèi)存柵欄,內(nèi)存柵障,屏障指令等, 是一類同步屏障指令,是CPU或編譯器在對(duì)
    發(fā)表于 11-14 09:43 ?6990次閱讀
    <b class='flag-5'>內(nèi)存</b><b class='flag-5'>屏障</b>是什么

    聊聊原子變量、內(nèi)存屏障那點(diǎn)(2)

    關(guān)于第一點(diǎn),關(guān)于指令重排,這里不考慮架構(gòu)的話,Load和Store兩種操作會(huì)有Load-Store、Store-Load、Load-Load、Store-Store這四種可能的亂序結(jié)果。 上文提到的三種屏障則是限制這些不同亂序的機(jī)制。
    的頭像 發(fā)表于 08-30 08:23 ?3962次閱讀

    Linux內(nèi)核的內(nèi)存屏障的原理和用法分析

    圈里流傳著一句話“珍愛(ài)生命,遠(yuǎn)離屏障”,這足以說(shuō)明內(nèi)存屏障是一個(gè)相當(dāng)晦澀和難以準(zhǔn)確把握的東西。使用過(guò)弱的屏障,會(huì)導(dǎo)致軟件不穩(wěn)定。
    的頭像 發(fā)表于 09-05 09:13 ?2952次閱讀

    Rust原子類型和內(nèi)存排序

    原子類型在構(gòu)建無(wú)數(shù)據(jù)結(jié)構(gòu),跨線程共享數(shù)據(jù),線程間同步等多線程并發(fā)編程場(chǎng)景中起到至關(guān)重要的作用。本文將從Rust提供的原子類型和原子類型的內(nèi)存
    的頭像 發(fā)表于 10-31 09:21 ?1596次閱讀

    一文徹底搞懂內(nèi)存屏障與volatile

    內(nèi)存屏障與 volatile 是高并發(fā)編程中比較常用的兩個(gè)技術(shù),無(wú)隊(duì)列的時(shí)候就會(huì)用到這兩項(xiàng)技術(shù)。然而這兩項(xiàng)技術(shù)涉及比較廣的基礎(chǔ)知識(shí),所以比較難以理解,也比較不容易解釋清楚。關(guān)于內(nèi)存
    的頭像 發(fā)表于 11-29 11:43 ?3334次閱讀

    小科普|聊聊網(wǎng)線那些

      網(wǎng)線這個(gè)東西,看著還真都差不多,插上就能用這能有什么差別呢?首先是線材本身,是分三六九等的,最常見(jiàn)的什么“超五類、六類、超六類”等,今天我們就跟著小編一起來(lái)聊聊網(wǎng)線那些。
    的頭像 發(fā)表于 07-26 10:18 ?2489次閱讀

    如何實(shí)現(xiàn)一個(gè)多讀多寫(xiě)的線程安全的無(wú)隊(duì)列

    加鎖。那么如何實(shí)現(xiàn)一個(gè)多讀多寫(xiě)的線程安全的無(wú)隊(duì)列呢? 互斥:mutexqueue(太簡(jiǎn)單不介紹了) 互斥+條件變量:blockqueue(太簡(jiǎn)單不介紹了)
    的頭像 發(fā)表于 11-08 15:25 ?2561次閱讀
    如何實(shí)現(xiàn)一個(gè)多讀多寫(xiě)的線程安全的無(wú)<b class='flag-5'>鎖</b>隊(duì)列