核心同步機制 優化屏障和記憶體屏障

2021-05-24 09:36:46 字數 3597 閱讀 2189

編譯器編譯源**時,會將源**進行優化,將源**的指令進行重排序,以適合於cpu的並行執行。然而,核心同步必須避免指令重新排序,優化屏障(optimization barrier)避免編譯器的重排序優化操作,保證編譯程式時在優化屏障之前的指令不會在優化屏障之後執行。

linux用巨集barrier實現優化屏障,gcc編譯器的優化屏障巨集定義列出如下(在include/linux/compiler-gcc.h中):

#define barrier() __asm__ __volatile__("": : :"memory")

上述定義中,「__asm__」表示插入了組合語言程式,「__volatile__」表示阻止編譯器對該值進行優化,確保變數使用了使用者定義的精確位址,而不是裝有同一資訊的一些別名。「memory」表示指令修改了記憶體單元。

軟體可通過讀寫屏障強制記憶體訪問次序。讀寫屏障像一堵牆,所有在設定讀寫屏障之前發起的記憶體訪問,必須先於在設定屏障之後發起的記憶體訪問之前完成,確保記憶體訪問按程式的順序完成。

讀寫屏障通過處理器構架的特殊指令mfence(記憶體屏障)、lfence(讀屏障)和sfence(寫屏障)完成,見《x86-64構架規範》一章。另外,在x86-64處理器中,對硬體進行操作的組合語言指令是「序列的」,也具有記憶體屏障的作用,如:對i/o埠進行操作的所有指令、帶lock字首的指令以及寫控制暫存器、系統暫存器或除錯暫存器的所有指令(如:cli和sti)。

linux核心提供的記憶體屏障api函式說明如表2。記憶體屏障可用於多處理器和單處理器系統,如果僅用於多處理器系統,就使用smp_***函式,在單處理器系統上,它們什麼都不要。

表2 記憶體屏障api函式說明

記憶體屏障的巨集定義

功能說明

mb()

適用於多處理器和單處理器的記憶體屏障。

rmb()

適用於多處理器和單處理器的讀記憶體屏障。

wmb()

適用於多處理器和單處理器的寫記憶體屏障。

smp_mb()

適用於多處理器的記憶體屏障。

smp_rmb()

適用於多處理器的讀記憶體屏障。

smp_wmb()

適用於多處理器的寫記憶體屏障。

適合於多處理器和單處理器的記憶體屏障巨集定義列出如下(在include/asm-x86/system.h中):

#ifdef config_x86_32

/*指令「lock; addl $0,0(%%esp)」表示加鎖,把0加到棧頂的記憶體單元,該指令操作本身無意義,但這些指令起到記憶體屏障的作用,讓前面的指令執行完成。具有xmm2特徵的cpu已有記憶體屏障指令,就直接使用該指令*/

#define mb() alternative("lock; addl $0,0(%%esp)", "mfence", x86_feature_xmm2)

#define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", x86_feature_xmm2)

#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", x86_feature_xmm)

#else

#define mb() asm volatile("mfence":::"memory")

#define rmb() asm volatile("lfence":::"memory")

#define wmb() asm volatile("sfence" ::: "memory")

#endif

/*重新整理後面的讀所依賴的所有掛起讀操作,在x86-64構架上不需要*/

#define read_barrier_depends() do while (0)

巨集定義ead_barrier_depends()重新整理後面的讀所依賴的所有掛起讀操作,後面的讀操作依賴於正處理的讀操作返回的資料。在x86-64構架上不需要此巨集。它表明:在此屏障之前,沒有來自記憶體區域資料所依賴的讀曾經重排序。所有的讀操作處理此原語,保證在跟隨此原語的任何讀操作此原語之前訪問記憶體(但不需要其他cpu的cache)。此原語在大多數cpu上有比rmb()更輕的份量。

本地cpu和編譯器遵循記憶體屏障的排序限制,僅記憶體屏障原語保證排序,即使資料有依賴關係,也不能保證排序。例如:下面**將強迫排序,因為*q的讀操作依賴於p的讀操作,並且這兩個讀操作被read_barrier_depends()分開。在cpu 0和cpu 1上執行的程式語句分別列出如下:

cpu 0                                      cpu 1

b = 2;

memory_barrier();

p = &b;                                      q = p;

read_barrier_depends();

d = *q;

下面的**沒有強制排序,因為在a和b的讀操作之間沒有依賴關係,因此,在一些cpu上,如:alpha,y將設定為3,x設定為0。類似這種沒有資料依賴關係的讀操作,需要排序應使用rmb()。

cpu 0                                        cpu 1

a = 2;

memory_barrier();

b = 3;                                         y = b;

read_barrier_depends();

x = a;

適合於多處理器的記憶體屏障巨集定義列出如下(在include/asm-x86/system.h中):

#ifdef config_smp

#define smp_mb() mb()

#ifdef config_x86_ppro_fence

# define smp_rmb() rmb()

#else

# define smp_rmb() barrier()

#endif

#ifdef config_x86_oostore

# define smp_wmb() wmb()

#else

# define smp_wmb() barrier()

#endif

#define smp_read_barrier_depends() read_barrier_depends()

#define set_mb(var, value) do while (0)

#else

#define smp_mb() barrier()

#define smp_rmb() barrier()

#define smp_wmb() barrier()

#define smp_read_barrier_depends() do while (0)

#define set_mb(var, value) do while (0)

#endif

函式rdtsc_barrier用於加記憶體屏障阻止rdtsc猜測,當在乙個定義的**區域使用讀取時間戳計數器(read time-stamp counter,rdtsc)函式(或者函式get_cycles或vread)時,必須加記憶體屏障阻止rdtsc猜測。其列出如下:

static inline void rdtsc_barrier(void)

優化屏障和記憶體屏障

優化屏障和記憶體屏障 優化屏障 編譯器編譯源 時,會將源 進行優化,將源 的指令進行重排序,以適合於cpu的並行執行。然而,核心同步必須避免指令重新排序,優化屏障 optimization barrier 避免編譯器的重排序優化操作,保證編譯程式時在優化屏障之前的指令不會在優化屏障之後執行。linu...

優化屏障和記憶體壁壘

當使用指令優化的編譯器時,你千萬不要認為指令會嚴格按它們在源 中出現的順序執行。例如,編譯器可能重新安排組合語言指令以使暫存器以最優的方式使用。此外,現代cpu通常並行地執行若干條指令,且可能重新安排記憶體訪問。這種重新排序可以極大地加速程式的執行。然而,當處理同步時,必須避免指令重新排序。因為如果...

優化屏障和記憶體壁壘

當使用指令優化的編譯器時,你千萬不要認為指令會嚴格按它們在源 中出現的順序執行。例如,編譯器可能重新安排組合語言指令以使暫存器以最優的方式使用。此外,現代cpu通常並行地執行若干條指令,且可能重新安排記憶體訪問。這種重新排序可以極大地加速程式的執行。然而,當處理同步時,必須避免指令重新排序。因為如果...