在Linux
設(shè)備驅(qū)動(dòng)中,我們必須要解決的一個(gè)問題是:多個(gè)進(jìn)程對(duì)共享資源的并發(fā)訪問,并發(fā)的訪問會(huì)導(dǎo)致競態(tài)。
并發(fā)(Concurrency)
:指的是多個(gè)執(zhí)行單元同時(shí)、并行的被執(zhí)行。
(相關(guān)資料圖)
競態(tài)(RaceConditions)
:并發(fā)執(zhí)行的單元對(duì)共享資源的訪問,容易導(dǎo)致競態(tài)。
共享資源:硬件資源和軟件上的全局變量、靜態(tài)變量等。
解決競態(tài)的途徑是:保證對(duì)共享資源的互斥訪問。
互斥訪問:一個(gè)執(zhí)行單元在訪問共享資源的時(shí)候,其他執(zhí)行單元被禁止訪問。
臨界區(qū)(Critical Sections)
:訪問共享資源的代碼區(qū)域成為臨界區(qū)。臨界區(qū)需要以某種互斥機(jī)制加以保護(hù)。
常見的互斥機(jī)制包括:中斷屏蔽,原子操作,自旋鎖,信號(hào)量,互斥體等。
image-20230511140139520
多對(duì)稱處理器(SMP)的多個(gè)CPU之間多個(gè)CPU使用共同的系統(tǒng)總線,可以訪問共同的外設(shè)和存儲(chǔ)器。在SMP
的情況下,多核(CPU0、CPU1)
的競態(tài)可能發(fā)生于:
CPU0
的進(jìn)程和CPU1
的進(jìn)程之間CPU0
的進(jìn)程和CPU1
的中斷之間CPU0
的中斷和CPU1
的中斷之間單CPU內(nèi),該進(jìn)程與搶占它的進(jìn)程之間在單CPU內(nèi),多個(gè)進(jìn)程并發(fā)執(zhí)行,當(dāng)一個(gè)進(jìn)程執(zhí)行的時(shí)間片耗盡,也有可能被另一個(gè)高優(yōu)先級(jí)進(jìn)程打斷,會(huì)發(fā)生競態(tài)。
中斷(軟中斷、硬中斷、Tasklet、底半部)與進(jìn)程之間當(dāng)一個(gè)進(jìn)程正在執(zhí)行,一個(gè)外部/內(nèi)部中斷(軟中斷、硬中斷、Tasklet等)將其打斷,會(huì)導(dǎo)致競態(tài)發(fā)生。
除了并發(fā)訪問導(dǎo)致的競態(tài)外,還需要了解編譯器和處理器的一些特點(diǎn)所引發(fā)的一些問題。
現(xiàn)代的高性能編譯器在目標(biāo)代碼優(yōu)化上都具有亂序優(yōu)化的能力,編譯器為了盡量 提高Cache命中率以及CPU的Load/Store單元的工作效率,可以對(duì)訪存的指令進(jìn)行亂序,減少邏輯上不必要的訪存。
因此,在打開編譯器優(yōu)化后,生成的匯編碼并沒有嚴(yán)格按照代碼的邏輯順序執(zhí)行,這是正常的。
為了解決編譯亂序的問題,可以加入barrier()
編譯屏障,
該屏障可以阻擋編譯器的優(yōu)化。設(shè)置屏障的前后,可以保證執(zhí)行的語句不亂。
加入barrier()
編譯屏障,即可保證正確的執(zhí)行順序。
例子:
#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;}
編譯亂序是編譯器的行為,而執(zhí)行亂序就是處理器運(yùn)行時(shí)的行為。
高級(jí)的CPU
往往會(huì)根據(jù)自身的緩存特性,將訪存指令重新排序執(zhí)行!這樣就導(dǎo)致了多個(gè)順序的指令,后發(fā)的指令仍有可能先執(zhí)行完畢。
這種執(zhí)行亂序,在多個(gè)
CPU
之間,以及單個(gè)CPU
內(nèi)部,都是非常常見的。
處理器為了解決多核之間,一個(gè)CPU
的行為對(duì)另一個(gè)CPU
可見的情況,ARM
處理器引入了內(nèi)存屏障指令:
Flush
流水線,保證所有在ISB之后執(zhí)行的指令都是從緩存或者內(nèi)存中獲得。在單
CPU
中,我們常遇到訪問外設(shè)寄存器時(shí),某些外設(shè)寄存器就對(duì)讀寫順序有很高的要求,為了避免執(zhí)行亂序的發(fā)生,這時(shí)候就需要CPU
的一些內(nèi)存屏障指令了。
CPU
內(nèi)部,為了解決這種問題,CPU
提供了一些內(nèi)存屏障指令:
讀寫屏障:可以參考
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
的區(qū)別就在于有無屏障。
由上文可知,為了解決
并發(fā)導(dǎo)致的競態(tài)問題高性能的編譯器編譯亂序問題高性能的CPU
帶來的執(zhí)行亂序問題CPU
和ARM
處理器提供的內(nèi)存屏障指令等,這也是內(nèi)核鎖存在的意義。
標(biāo)簽: