class="152959">
本文詳細的介紹了Linux內核中的同步機制:原子操作、信號量、讀寫信號量和自旋鎖的API,使用要求以及一些典型示例
一、引言 在現代操作系統裡,同一時間可能有多個內核執行流在執行,因此內核其實象多進程多線程編程一樣也需要一些同步機制來同步各執行單元對共享數據的訪問。尤其是在多處理器系統上,更需要一些同步機制來同步不同處理器上的執行單元對共享的數據的訪問。
在主流的Linux內核中包含了幾乎所有現代的操作系統具有的同步機制,這些同步機制包括:原子操作、信號量(semaphore)、讀寫信號量(rw_semaphore)、spinlock、BKL(Big Kernel Lock)、rwlock、brlock(只包含在2.4內核中)、RCU(只包含在2.6內核中)和seqlock(只包含在2.6內核中)。
二、原子操作 所謂原子操作,就是該操作絕不會在執行完畢前被任何其他任務或事件打斷,也就說,它的最小的執行單位,不可能有比它更小的執行單位,因此這裡的原子實際是使用了物理學裡的物質微粒的概念。
原子操作需要硬件的支持,因此是架構相關的,其API和原子類型的定義都定義在內核源碼樹的include/asm/atomic.h文件中,它們都使用匯編語言實現,因為C語言並不能實現這樣的操作。
原子操作主要用於實現資源計數,很多引用計數(refcnt)就是通過原子操作實現的。原子類型定義如下:
typedef struct { volatile int counter; } 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實現)。