原子操作簡介:
原子操作就是:指不能再進一步分割的操作一般原子操作用於:<
整形操作
>或者<位操作
>。假如現在要對無符號整形變數 a 賦值,值為3,對於 c 語言來講很簡單,直接就是:a = 3
但是 c 語言要先編譯為成彙編指令,arm 架構不支援直接對暫存器進行讀寫操作,比如 要借助暫存器 r0、r1 等來完成賦值操作。假設變數 a 的位址為 0x3000000,「a=3」這一行 c 語言可能會被編譯為如下所示的彙編**:假設現在ldr r0, =0x30000000 /* 變數 a 位址 /
ldr r1, = 3 / 要寫入的值 /
str r1, [r0] / 將 3 寫入到 a 變數中 */
執行緒 a
要向 a 變數寫入 10 這個值,而執行緒 b
也要向 a 變數寫入 20 這個值,我們理想中的執行順序:
確實可以實現執行緒 a
將 a 變數設定為 10,執行緒 b
將 a 變數設定為 20。但是實際上的執行流程可能如圖 下 所示:
1、原子整形操作 api 函式:執行緒 a
最終將變數 a 設定為了 20,而並不是要求的 10!執行緒b
沒有問題。這就是乙個最簡單的設定變數值的併發與競爭的例子,要解決這個問題就要保證**a=10
中的三行彙編指令作為乙個整體執行,也就是作為乙個原子存在。linux核心提供了一組原子操作 api
函式來完成此功能,linux 核心提供了兩組原子操作 api 函式
,一組是對整形變數進行操作的,一組是對位進行操作的,我們接下來看一下這些 api 函式。
linux 核心定義了叫做atomic_t
的結構體來完成整形資料
的原子操作,在使用中用原子變數
來代替整形變數
,此結構體定義在include/linux/types.h
檔案中,定義如下:
typedef struct atomic_t;
如果要使用原子操作 api 函式,首先要先定義乙個atomic_t
型別的變數,如下所示:
atomic_t a; //定義 a
也可以在定義原子變數的時候通過巨集atomic_init()
向原子變數賦初值給原子變數賦初值,如下所示:
atomic_t b = atomic_init(0)
; //定義原子變數 b 並賦初值為 0
原子變數有了,接下來就是對原子變數進行操作,比如讀、寫、增加、減少
等等,linux 內
核提供了大量的原子操作 api 函式
,如下所示:
注意:如果使用
64 位的 soc
的話,就要用到64
位的原子變數,linux 核心也定義了 64 位原子 結構體,如下所示:
typedef struct atomic64_t;
相應的也提供了 64 位原子變數的操作 api 函式,這裡就不講了,和表中的 api函式有用法一樣,只是將「
atomic_
」字首換為「atomic64_
」,將int
換為long long
。
atomic_t v =
atomic_init(0
);/* 定義並初始化原子變零 v=0 */
atomic_set(10
);/* 設定 v=10 */
atomic_read
(&v)
;/* 讀取 v 的值,肯定是 10 */
atomic_inc
(&v)
;/* v 的值加 1,v=11 */
2、原子位操作 api 函式:
位操作也是很常用的操作,linux 核心也提供了一系列的原子位操作 api 函式
,只不過原 子位操作不像原子整形變數那樣有個atomic_t
的資料結構,原子位操作
是直接對記憶體
進行操作, api 函式如表 下 所示:
解決競態問題之訊號量
1 訊號量簡介 訊號量是同步的一種方式,linux 核心也提供了訊號量機制,訊號量常常用於控制對共享資源的訪問。舉乙個很常見的例子,某個停車場有 100個停車位,這 100 個停車位大家都可以用,對於大家來說這100 個停車位就是共享資源。假設現在這個停車場正常執行,你要把車停到這個這個停車場肯定要...
linux作業系統之競態條件(時序競態)
1 時序競態 前後兩次執行同乙個程式,出現的結果不同。2 pause函式 使用該函式會造成程序主動掛起,並等待訊號喚醒,呼叫該系統呼叫的程序會處於阻塞狀態 主動放棄cpu 函式原型 int pause void 返回值為 1,並設定errno為eintr 使用pause和alarm實現sleep函式...
原子性 競態條件 加鎖機制
首先直接上 下面是一段servlet public long getcount public void service servletrequest req servletresponse resp 上面這個 是為了統計有多個次訪問了servlet。但是該類是執行緒不安全的,因為 count看上去只...