LINUX内核之内存屏障
@CopyLeft by ICANTH,I Can do ANy THing that I CAN THink!~
Author: WenHui, WuHan University,2012-6-4
?
內存屏障(Memory Barriers)
一方面,CPU由于采用指令流水線和超流水線技術,可能導致CPU雖然順序取指令、但有可能會出現“亂序”執行的情況,當然,對于” a++;b = f(a);c = f”等存在依賴關系的指令,CPU則會在“b= f(a)”執行階段之前被阻塞;另一方面,編譯器也有可能將依賴關系很近“人為地”拉開距離以防止阻塞情況的發生,從而導致編譯器亂序,如“a++ ;c = f;b = f(a)”。
一個CPU對指令順序提供如下保證:
(1) On any given CPU, dependent memory accesses will be issued in order, with respect to itself.如Q = P; D = *Q;將保證其順序執行
(2) Overlapping loads and stores within a particular CPU will appear to be ordered within that CPU.重疊的Load和Store操作將保證順序執行(目標地址相同的Load、Store),如:a = *X; *X = b;
(3) It _must_not_ be assumed that independent loads and stores will be issued in the order given.
(4) It _must_ be assumed that overlapping memory accesses may be merged or discarded.如*A = X; Y = *A; => STORE *A = X; Y = LOAD *A; / or STORE *A = Y = X;
?
由此可見,無關的內存操作會被按隨機順序有效的得到執行,但是在CPU與CPU交互時或CPU與IO設備交互時, 這可能會成為問題. 我們需要一些手段來干預編譯器和CPU, 使其限制指令順序。內存屏障就是這樣的干預手段. 他們能保證處于內存屏障兩邊的內存操作滿足部分有序.(譯注: 這里"部分有序"的意思是, 內存屏障之前的操作都會先于屏障之后的操作, 但是如果幾個操作出現在屏障的同一邊, 則不保證它們的順序.)
(1) 寫(STORE)內存屏障。在寫屏障之前的STORE操作將先于所有在寫屏障之后的STORE操作。
(2) 數據依賴屏障。兩條Load指令,第二條Load指令依賴于第一條Load指令的結果,則數據依賴屏障保障第二條指令的目標地址將被更新。
(3) 讀(LOAD)內存屏障。讀屏障包含數據依賴屏障的功能, 并且保證所有出現在屏障之前的LOAD操作都將先于所有出現在屏障之后的LOAD操作被系統中的其他組件所感知.
(4) 通用內存屏障. 通用內存屏障保證所有出現在屏障之前的LOAD和STORE操作都將先于所有出現在屏障之后的LOAD和STORE操作被系統中的其他組件所感知.
(5) LOCK操作.它的作用相當于一個單向滲透屏障.它保證所有出現在LOCK之后的內存操作都將在LOCK操作被系統中的其他組件所感知之后才能發生. 出現在LOCK之前的內存操作可能在LOCK完成之后才發生.LOCK操作總是跟UNLOCK操作配對出現.
(6) UNLOCK操作。它保證所有出現在UNLOCK之前的內存操作都將在UNLOCK操作被系統中的其他組件所感知之前發生.
? LINUX對于x86而言,在為UP體系統架構下,調用barrier()進行通用內存屏障。在SMP體系架構下,若為64位CPU或支持mfence、lfence、sfence指令的32位CPU,則smp_mb()、smp_rmb()、smp_smb()對應通用內存屏障、寫屏障和讀屏障;而不支持mfence、lfence、sfence指令的32位CPU則smp_mb()、smp_rmb()、smp_smb()對應LOCK操作。源碼請參見《內存屏障源碼分析》一節。 ?內存屏障源碼分析
/include/asm-generic/system.h: 053 #ifdef CONFIG_SMP 054 #define smp_mb()??????? mb() 055 #define smp_rmb()?????? rmb() 056 #define smp_wmb()?????? wmb() 057 #else 058 #define smp_mb()??????? barrier() 059 #define smp_rmb()?????? barrier() 060 #define smp_wmb()?????? barrier() 061 #endif在x86 UP體系架構中,smp_mb、smp_rmb、smp_wmb被翻譯成barrier:
012 #define barrier() __asm__ __volatile__("": : :"memory")__volatile告訴編譯器此條語句不進行任何優化,"": : :"memory" 內存單元已被修改、需要重新讀入。
?
在x86 SMP體系架構中,smp_mb、smp_rmb、smp_wmb如下定義:
/arch/x86/include/asm/system.h:
352 /* 353? * Force strict CPU ordering. 354? * And yes, this is required on UP too when we're talking 355? * to devices. 356? */ 357 #ifdef CONFIG_X86_32 358 /* 359? * Some non-Intel clones support out of order store. wmb() ceases to be a 360? * nop for these. 361? */ 362 #define mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2) 363 #define rmb() alternative("lock; addl $0,0(%%esp)", "lfence", X86_FEATURE_XMM2) 364 #define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM) 365 #else 366 #define mb()??? asm volatile("mfence":::"memory") 367 #define rmb()?? asm volatile("lfence":::"memory") 368 #define wmb()?? asm volatile("sfence" ::: "memory") 369 #endif362~364行針對x86的32位CPU,366~368行針對x86的64位CPU。
?
在x86的64位CPU中,mb()宏實際為: asm volatile("sfence" ::: "memory")。 volatile告訴編譯器嚴禁在此處匯編語句與其它語句重組優化,memory強制編譯器假設RAM所有內存單元均被匯編指令修改,"sfence" ::: 表示在此插入一條串行化匯編指令sfence。 mfence:串行化發生在mfence指令之前的讀寫操作 lfence:串行化發生在mfence指令之前的讀操作、但不影響寫操作 sfence:串行化發生在mfence指令之前的寫操作、但不影響讀操作 ? 在x86的32位CPU中,mb()宏實際為:mb() alternative("lock; addl $0,0(%%esp)", "mfence", X86_FEATURE_XMM2)
由于x86的32位CPU有可能不提供mfence、lfence、sfence三條匯編指令的支持,故在不支持mfence的指令中使用:"lock; addl $0,0(%%esp)", "mfence"。lock表示將“addl $0,0(%%esp)”語句作為內存屏障。
關于lock的實現:cpu上有一根pin #HLOCK連到北橋,lock前綴會在執行這條指令前先去拉這根pin,持續到這個指令結束時放開#HLOCK pin,在這期間,北橋會屏蔽掉一切外設以及AGP的內存操作。也就保證了這條指令的atomic。 ?參考資料
《memroy-barries.txt》,/Documentation/memory-barriers.txt
《LINUX內核內存屏障》,http://blog.csdn.net/ljl1603/article/details/6793982
posted on 2012-06-10 21:31 我思我能 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/icanth/archive/2012/06/10/2544300.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的LINUX内核之内存屏障的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ibatis查询条件对于特殊字符的处理方
- 下一篇: 编译linux内核时出错