併發是指的是多個執行單元同時被執行,而併發的執行單元對共享資源(硬體資源和軟體上的全域性變數、靜態變數等)的訪問很容易導致竟態。主要有以下三個方面:
一、對稱處理器的多個cpu。
二、單cpu內,程序與搶占它的程序
三、中斷可能被其他的程序中斷。而這個正是我們的重點。
而防止中斷的的方法主要是:
(1)遮蔽中斷
local_irq_disable()/*遮蔽中斷*/
critical section ... /*這是臨界區*/
local_irq_enable()/*開中斷*/
也可以單獨禁止中斷的底半部。
(2)原子操作
所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,它的最小的執行單位,不可能有比它更小的執行單位,因此這裡的原子實際是使用了物理學裡的物質微粒的概念。
原子操作需要硬體的支援,因此是架構相關的,其api和原子型別的定義都定義在核心原始碼樹的include/asm/atomic.h檔案中,它們都使用組合語言實現,因為c語言並不能實現這樣的操作。
原子操作主要用於實現資源計數,很多引用計數(refcnt)就是通過原子操作實現的。原子型別定義如下:
typedef struct atomic_t;
volatile修飾字段告訴gcc不要對該型別的資料做優化處理,對它的訪問都是對記憶體的訪問,而不是對暫存器的訪問。
原子操作api包括:
atomic_read(atomic_t * v);
該函式對原子型別的變數進行原子讀操作,它返回原子型別的變數v的值。
atomic_set(atomic_t * v, int i);
該函式設定原子型別的變數v的值為i。
void atomic_add(int i, atomic_t *v);
該函式給原子型別的變數v增加值i。
atomic_sub(int i, atomic_t *v);
該函式從原子型別的變數v中減去i。
int atomic_sub_and_test(int i, atomic_t *v);
該函式從原子型別的變數v中減去i,並判斷結果是否為0,如果為0,返回真,否則返回假。
void atomic_inc(atomic_t *v);
該函式對原子型別變數v原子地增加1。
void atomic_dec(atomic_t *v);
該函式對原子型別的變數v原子地減1。
int atomic_dec_and_test(atomic_t *v);
該函式對原子型別的變數v原子地減1,並判斷結果是否為0,如果為0,返回真,否則返回假。
int atomic_inc_and_test(atomic_t *v);
該函式對原子型別的變數v原子地增加1,並判斷結果是否為0,如果為0,返回真,否則返回假。
int atomic_add_negative(int i, atomic_t *v);
該函式對原子型別的變數v原子地增加i,並判斷結果是否為負數,如果是,返回真,否則返回假。
int atomic_add_return(int i, atomic_t *v);
該函式對原子型別的變數v原子地增加i,並且返回指向v的指標。
int atomic_sub_return(int i, atomic_t *v);
該函式從原子型別的變數v中減去i,並且返回指向v的指標。
int atomic_inc_return(atomic_t * v);
該函式對原子型別的變數v原子地增加1並且返回指向v的指標。
int atomic_dec_return(atomic_t * v);
該函式對原子型別的變數v原子地減1並且返回指向v的指標。
原子操作通常用於實現資源的引用計數,在tcp/ip協議棧的ip碎片處理中,就使用了引用計數,碎片佇列結構struct ipq描述了乙個ip碎片,欄位refcnt就是引用計數器,它的型別為atomic_t,當建立ip碎片時(在函式ip_frag_create中),使用atomic_set函式把它設定為1,當引用該ip碎片時,就使用函式atomic_inc把引用計數加1。
當不需要引用該ip碎片時,就使用函式ipq_put來釋放該ip碎片,ipq_put使用函式atomic_dec_and_test把引用計數減1並判斷引用計數是否為0,如果是就釋放ip碎片。函式ipq_kill把ip碎片從ipq佇列中刪除,並把該刪除的ip碎片的引用計數減1(通過使用函式atomic_dec實現)。
(3)配合中斷一起使用的自旋鎖
Linux 竟態條件和SIGCHLD
乙個函式被多個執行流呼叫,有可能在第一次呼叫還沒返回時就再次進入函式,稱為重入。例如上面的insert函式,在執行第一步時收到訊號產生硬體中斷,轉去訊號處理函式,而訊號處理函恰好呼叫了insert函式,由於兩個函式操縱了同乙個鍊錶而產生意外的結果,所以這個函式是不可重入函式。如果這個函式只是操縱自己...
Linux中的併發和競態
本帖大體上描述linux kernel為解決併發導致的競態所提供的核心api 主要是訊號量和自旋鎖 之間的區別,側重於使用方面。級的閱讀比較打算另開一貼。因為程式的併發執行而導致的競態是linux核心中乙個非常複雜的方面。對於裝置的驅動程式開發者而言,熟悉linux核心提供的併發互斥的處理機制相當重...
Linux中的併發和競態
本帖大體上描述linux kernel為解決併發導致的競態所提供的核心api 主要是訊號量和自旋鎖 之間的區別,側重於使用方面。級的閱讀比較打算另開一貼。因為程式的併發執行而導致的競態是linux核心中乙個非常複雜的方面。對於裝置的驅動程式開發者而言,熟悉linux核心提供的併發互斥的處理機制相當重...