
在Linux
設備驅動中,我們必須要解決的一個問題是:多個進程對共享資源的并發訪問,并發的訪問會導致競態。
并發(Concurrency)
:指的是多個執行單元同時、并行的被執行。
(相關資料圖)
競態(RaceConditions)
:并發執行的單元對共享資源的訪問,容易導致競態。
共享資源:硬件資源和軟件上的全局變量、靜態變量等。
解決競態的途徑是:保證對共享資源的互斥訪問。
互斥訪問:一個執行單元在訪問共享資源的時候,其他執行單元被禁止訪問。
臨界區(Critical Sections)
:訪問共享資源的代碼區域成為臨界區。臨界區需要以某種互斥機制加以保護。
常見的互斥機制包括:中斷屏蔽,原子操作,自旋鎖,信號量,互斥體等。
image-20230511140139520
多對稱處理器(SMP)的多個CPU之間多個CPU使用共同的系統總線,可以訪問共同的外設和存儲器。在SMP
的情況下,多核(CPU0、CPU1)
的競態可能發生于:
CPU0
的進程和CPU1
的進程之間CPU0
的進程和CPU1
的中斷之間CPU0
的中斷和CPU1
的中斷之間單CPU內,該進程與搶占它的進程之間在單CPU內,多個進程并發執行,當一個進程執行的時間片耗盡,也有可能被另一個高優先級進程打斷,會發生競態。
中斷(軟中斷、硬中斷、Tasklet、底半部)與進程之間當一個進程正在執行,一個外部/內部中斷(軟中斷、硬中斷、Tasklet等)將其打斷,會導致競態發生。
除了并發訪問導致的競態外,還需要了解編譯器和處理器的一些特點所引發的一些問題。
現代的高性能編譯器在目標代碼優化上都具有亂序優化的能力,編譯器為了盡量 提高Cache命中率以及CPU的Load/Store單元的工作效率,可以對訪存的指令進行亂序,減少邏輯上不必要的訪存。
因此,在打開編譯器優化后,生成的匯編碼并沒有嚴格按照代碼的邏輯順序執行,這是正常的。
為了解決編譯亂序的問題,可以加入barrier()
編譯屏障,
該屏障可以阻擋編譯器的優化。設置屏障的前后,可以保證執行的語句不亂。
加入barrier()
編譯屏障,即可保證正確的執行順序。
例子:
#define barrier() __asm__ __volatile__("": : :"memory")int main(int argc,char *argv[]){ int a = 0,b,c,d[4096],e; e = d[4095]; barrier(); b = a; c = a; return 0;}
編譯亂序是編譯器的行為,而執行亂序就是處理器運行時的行為。
高級的CPU
往往會根據自身的緩存特性,將訪存指令重新排序執行!這樣就導致了多個順序的指令,后發的指令仍有可能先執行完畢。
這種執行亂序,在多個
CPU
之間,以及單個CPU
內部,都是非常常見的。
處理器為了解決多核之間,一個CPU
的行為對另一個CPU
可見的情況,ARM
處理器引入了內存屏障指令:
Flush
流水線,保證所有在ISB之后執行的指令都是從緩存或者內存中獲得。在單
CPU
中,我們常遇到訪問外設寄存器時,某些外設寄存器就對讀寫順序有很高的要求,為了避免執行亂序的發生,這時候就需要CPU
的一些內存屏障指令了。
CPU
內部,為了解決這種問題,CPU
提供了一些內存屏障指令:
讀寫屏障:可以參考
Documentation/memory-devices.txt
和Documentation/io_ordering.txt
mb()
讀屏障:rmb()
寫屏障:wmb()
寄存器讀屏障__iormb()__
寄存器寫屏障__iowmb()__
#define writeb_relaxed(v,c) __raw_writeb(v,c)#define writew_relaxed(v,c) __raw_writew((__force u16) cpu_to_le16(v),c)#define writel_relaxed(v,c) __raw_writel((__force u32) cpu_to_le32(v),c)#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })
writel
與writel_relaxed
的區別就在于有無屏障。
由上文可知,為了解決
并發導致的競態問題高性能的編譯器編譯亂序問題高性能的CPU
帶來的執行亂序問題CPU
和ARM
處理器提供的內存屏障指令等,這也是內核鎖存在的意義。
標簽: