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。
