內存屏障 & Memory barrier


Memory Barrier

http://www.wowotech.net/kernel_synchronization/memory-barrier.html

這里面講了Memory Barrier

對於一個c程序員,我們的編寫的代碼能所見即所得嗎?我們看到的c程序的邏輯是否就是最后CPU運行的結果呢?很遺憾,不是,我們的“所見”和最后的執行結果隔着:

1、編譯器

2、CPU取指執行

編譯器了解底層CPU的思維模式,因此,它可以在將c翻譯成匯編的時候進行優化(例如內存訪問指令的重新排序),讓產出的匯編指令在CPU上運行的時候更快。然而,這種優化產出的結果未必符合程序員原始的邏輯,因此,作為程序員,作為c程序員,必須有能力了解編譯器的行為,並在通過內嵌在c代碼中的memory barrier來指導編譯器的優化行為(這種memory barrier又叫做優化屏障,Optimization barrier),讓編譯器產出即高效,又邏輯正確的代碼。

 

我們先看下面的一個例子:

preempt_disable()

臨界區

preempt_enable

 

我們知道所謂的preempt enable和disable其實就是對當前進程的struct thread_info中的preempt_count進行加一和減一的操作。具體的代碼如下:

#define preempt_disable() \ 
do { \ 
    preempt_count_inc(); \ 
    barrier(); \ 
} while (0)

 

使用do...while(0)的好處可見:

http://www.cnblogs.com/charlesblc/p/6080315.html

 

linux kernel中的定義和我們的想像一樣,除了barrier這個優化屏障。barrier就象是c代碼中的一個柵欄,將代碼邏輯分成兩段,barrier之前的代碼和barrier之后的代碼在經過編譯器編譯后順序不能亂掉。也就是說,barrier之后的c代碼對應的匯編,不能跑到barrier之前去,反之亦然。之所以這么做是因為在我們這個場景中,如果編譯為了榨取CPU的performace而對匯編指令進行重排,那么臨界區的代碼就有可能位於preempt_count_inc之外,從而起不到保護作用。

 

barrier是否夠呢?

對於multi-core的系統,只有當該task被調度到該CPU上執行的時候,該CPU才會訪問該task的preempt count,因此對於preempt enable和disable而言,不存在多個CPU同時訪問的場景。

但是,即便這樣,如果CPU是亂序執行(out-of-order excution)的呢?其實,我們也不用擔心,正如前面敘述的,preempt count這個memory實際上是不存在多個cpu同時訪問的情況,因此,它實際上會本cpu的進程上下文和中斷上下文訪問。能終止當前thread執行preempt_disable的只有中斷。為了方便描述,我們給代碼編址,如下:

 

地址 該地址的匯編指令 CPU的執行順序
a preempt_disable() 臨界區指令1
a+4 臨界區指令1 preempt_disable()
a+8 臨界區指令2 臨界區指令2
a+12 preempt_enable preempt_enable

當發生中斷的時候,硬件會獲取當前PC值,並精確的得到了發生指令的地址。有兩種情況:

(1)在地址a發生中斷。對於out-of-order的CPU,臨界區指令1已經執行完畢,preempt_disable正在pipeline中等待執行。由於是在a地址發生中斷,也就是preempt_disable地址上發生中斷,對於硬件而言,它會保證a地址之前(包括a地址)的指令都被執行完畢,並且a地址之后的指令都沒有執行。因此,在這種情況下,臨界區指令1的執行結果被拋棄掉,因此,實際臨界區指令不會先於preempt_disable執行

(2)在地址a+4發生中斷。這時候,雖然發生中斷的那一刻的地址上的指令(臨界區指令1)已經執行完畢了,但是硬件會保證地址a+4之前的所有的指令都執行完畢,因此,實際上CPU會執行完preempt_disable,然后跳轉的中斷異常向量執行。

 

注意:如果CPU是亂序執行(out-of-order excution)的,barrier只是保證compiler輸出的匯編指令的順序是OK的,不能確保CPU執行時候的亂序。 

CPU會亂排,但是有的順序不會調換,根據load和store型指令,不同處理器的策略不同,可以見:

Java內存模型(可以結合着看)

http://www.cnblogs.com/charlesblc/p/6126551.html

 

 對這個問題的回答來自ARM architecture的內存訪問模型:對於program order是A1-->A2的情況(A1和A2都是對Device或是Strongly-ordered的memory進行訪問的指令),ARM保證A1也是先於A2執行的。因此,在這樣的場景下,使用barrier足夠了。 對於X86也是類似的,雖然它沒有對IO space采樣memory mapping的方式,但是,X86的所有操作IO端口的指令都是被順執行的,不需要考慮memory access order。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM