linux 中的原子操作和內存屏蔽

由於操作系統中存在多進程對共享資源的併發訪問,從而引起了進程間的競態。這其中包括了我們所熟知的SMP系統,多核間的相互競爭資源,單CPU之間的相互競爭,中斷和進程間的相互搶佔等諸多問題。

因此,我們需要一些解決方法,在Linux內核中它提供瞭如下幾種鎖機制,供用戶在針對不同情況分別或配合使用,包括:原子操作內存屏障自旋鎖讀寫自旋鎖順序鎖信號量讀寫信號量完成量RCU機制BKL(大內核鎖)等等

一、原子操作

所謂原子操作,就是“不可中斷的一個或一系列操作”。不同構架的cpu具體實現方式不同,對於單核cpu沒有意義

Linux內核提供了兩組原子操作接口:一組是針對整數進行操作;另一組是針對單獨的位進行操作。

1、原子整數操作

atomic_read(v) 讀取v指向的原子變量的值。

atomic_set(v,i) 設置v指向的原子變量的值爲i。

void atomic_sub(int i,atomic_t *v) 從v指向的原子變量減去i。

void atomic_inc(atomic_t *v) 遞增v指向的原子變量。

void atomic_dec(atomic_t *v) 遞減v指向的原子變量。

int atomic_dec_and_test(atomic_t *v) 遞減v指向的原子變量,並測試是否爲0。若爲0,返回真,否則返回假。

int atomic_inc_and_test(atomic_t *v) 遞增v指向的原子變量,並測試是否爲0。若爲0,返回真,否則返回假。

int atomic_add_negative(int i,atomic_t *v) 將v指向的原子變量加上i,並測試結果是否爲負。若爲負,返回真。這個操作用於實現semaphore。

2、原子位操作

void set_bit(int nr, void *addr) 原子設置addr所指的第nr位

void clear_bit(int nr, void *addr) 原子的清空所指對象的第nr位

void change_bit(nr, void *addr) 原子的翻轉addr所指的第nr位

int test_bit(nr, void *addr) 原子的返回addr位所指對象nr位

int test_and_set_bit(nr, void *addr) 原子設置addr所指對象的第nr位,並返回原先的值

int test_and_clear_bit(nr, void *addr) 原子清空addr所指對象的第nr位,並返回原先的值

int test_and_change_bit(nr, void *addr) 原子翻轉addr所指對象的第nr位,並返回原先的值

二、內存屏蔽

值得一提的是,使用這些內存屏障,相當於停用了處理器或編譯器提供的優化機制,顯然這樣的結果必然影響程序的性能。

內存屏障,從處理器角度來說,是用來串行化讀寫操作的,從軟件角度來講,就是用來解決順序一致性問題的。編譯器不是要打亂代碼執行順序嗎,處理器不是要亂序執行嗎,你插入一個內存屏障,就相當於告訴編譯器,屏障前後的指令順序不能顛倒,告訴處理器,只有等屏障前的指令執行完了,屏障後的指令才能開始執行。當然,內存屏障能阻擋編譯器亂來,但處理器還是有辦法。處理器中不是有多發射、亂序執行、順序完成的概念嗎,它在內存屏障時只要保證前面指令的讀寫操作,一定在後面指令的讀寫操作完成之前完成,就可以了。所以內存屏障纔會對應有讀屏障、寫屏障和讀寫屏障三類。如x86之前保證寫操作都是順序完成的,所以不需要寫屏障,但現在也有部分ia32處理器的寫操作變成亂序完成,所以也需要寫屏障。

其實,除了專門的讀寫屏障指令,還有很多指令的執行是帶有讀寫屏障功能的,比如帶lock前綴的指令。在專門的讀寫屏障指令出現之前,linux就是靠lock熬過來的。
至於在那裏插入讀寫屏障,要視軟件的需求而定。讀寫屏障無法完全實現順序一致性,但多處理器上的線程也不會一直盯着你的執行順序看,只要保證在它看過來的時候,認爲你符合順序一致性,執行不會出現你代碼中沒有預料到的情況。所謂預料外的情況,舉例而言,你的線程是先給變量a賦值,再給變量b賦值,結果別的處理器上運行的線程看過來,發現b賦值了,a卻沒有賦值,(注意這種不一致不是由緩存不一致造成的,而是處理器寫操作完成的順序不一致造成的),這時就要在a賦值與b賦值之間,加一個寫屏障。

mb() 適用於多處理器和單處理器的內存屏障。

rmb() 適用於多處理器和單處理器的讀內存屏障。

wmb() 適用於多處理器和單處理器的寫內存屏障。

smp_mb() 適用於多處理器的內存屏障。

smp_rmb() 適用於多處理器的讀內存屏障。

smp_wmb() 適用於多處理器的寫內存屏障。

三、順序鎖

順序鎖是對讀寫鎖的一種優化。

1.讀執行單元絕對不會被寫執行單元阻塞。即讀執行單元可以在寫執行單元對被順序鎖保護的共享資源進行寫操作的同時仍然可以繼續讀,而不必等待寫執行單元完成之後再去讀,同樣,寫執行單元也不必等待所有的讀執行單元讀完之後纔去進行寫操作

2.寫執行單元與寫執行單元之間仍然是互斥的。

3.如果讀執行單元在讀操作期間,寫執行單元已經發生了寫操作,那麼,讀執行單元必須重新去讀數據,以便確保讀到的數據是完整的。

4.要求共享資源中不能含有指針。


seqlock_init(x);       //動態初始化

DEFINE_SEQLOCK(x);     //靜態初始化

void write_seqlock(seqlock_t* sl); //寫加鎖

int write_tryseqlock(seqlock_t* sl); //嘗試寫加鎖

write_seqlock_irqsave(lock, flags); //local_irq_save() + write_seqlock()

write_seqlock_irq(lock); //local_irq_disable() + write_seqlock()

write_seqlock_bh(lock); //local_bh_disable() + write_seqlock()

void write_sequnlock(seqlock_t* sl); //寫解鎖

write_sequnlock_irqrestore(lock, flags); //write_sequnlock() + local_irq_restore()

write_sequnlock_irq(lock); //write_sequnlock() + local_irq_enable()

write_sequnlock_bh(lock); //write_sequnlock() + local_bh_enable()


參考:http://blog.sina.com.cn/s/blog_6d7fa49b01014q7p.html

發佈了34 篇原創文章 · 獲贊 5 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章