LInux 的併發和竟態 中斷 原子操作 自旋鎖

2021-06-02 09:25:18 字數 2606 閱讀 9812

併發是指的是多個執行單元同時被執行,而併發的執行單元對共享資源(硬體資源和軟體上的全域性變數、靜態變數等)的訪問很容易導致竟態。主要有以下三個方面:

一、對稱處理器的多個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核心提供的併發互斥的處理機制相當重...