關於單CPU,多CPU上的原子操作

2022-05-02 07:09:10 字數 2806 閱讀 7132

所謂原子操作,就是"不可中斷的乙個或一系列操作" 。

硬體級的原子操作:

在單處理器系統(uniprocessor)中,能夠在單條指令中完成的操作都可以認為是" 原子操作",因為中斷只能發生於指令之間。這也是某些cpu指令系統中引入了test_and_set、test_and_clear等指令用於臨界資源互斥的原因。

在對稱多處理器(symmetric multi-processor)結構中就不同了,由於系統中有多個處理器在獨立地執行,即使能在單條指令中完成的操作也有可能受到干擾。

在x86 平台上,cpu提供了在指令執行期間對匯流排加鎖的手段。cpu晶元上有一條引線#hlock pin,如果組合語言的程式中在一條指令前面加上字首"lock",經過彙編以後的機器**就使cpu在執行這條指令的時候把#hlock pin的電位拉低,持續到這條指令結束時放開,從而把匯流排鎖住,這樣同一匯流排上別的cpu就暫時不能通過匯流排訪問記憶體了,保證了這條指令在多處理器環境中的

原子性。

軟體級的原子操作:

軟體級的原子操作實現依賴於硬體原子操作的支援。

對於linux而言,核心提供了兩組原子操作介面:一組是針對整數進行操作;另一組是針對單獨的位進行操作。

2.1. 原子整數操作

針對整數的原子操作只能對atomic_t型別的資料處理。這裡沒有使用c語言的int型別,主要是因為:

1) 讓原子函式只接受atomic_t型別運算元,可以確保原子操作只與這種特殊型別資料一起使用

2) 使用atomic_t型別確保編譯器不對相應的值進行訪問優化

3) 使用atomic_t型別可以遮蔽不同體系結構上的資料型別的差異。儘管linux支援的所有機器上的整型資料都是32位,但是使用atomic_t的**只能將該型別的資料當作24位來使用。這個限制完全是因為在sparc體系結構上,原子操作的實現不同於其它體系結構:32位int型別的低8位嵌入了乙個鎖,因為sparc體系結構對原子操作缺乏指令級的支援,所以只能利用該鎖來避免對原子型別資料的併發訪問。

原子整數操作最常見的用途就是實現計數器。原子整數操作列表在中定義。原子操作通常是內斂函式,往往通過內嵌彙編指令來實現。如果某個函式本來就是原子的,那麼它往往會被定義成乙個巨集。

在編寫核心時,操作也簡單:

atomic_t use_cnt;

atomic_set(&use_cnt, 2);

atomic_add(4, &use_cnt);

atomic_inc(use_cnt);

2.2. 原子性與順序性

原子性確保指令執行期間不被打斷,要麼全部執行,要麼根本不執行。而順序性確保即使兩條或多條指令出現在獨立的執行執行緒中,甚至獨立的處理器上,它們本該執行的順序依然要保持。

2.3. 原子位操作

原子位操作定義在檔案中。令人感到奇怪的是位操作函式是對普通的記憶體位址進行操作的。原子位操作在多數情況下是對乙個字長的記憶體訪問,因而位號該位於0-31之間(在64位機器上是0-63之間),但是對位號的範圍沒有限制。

編寫核心**,只要把指向了你希望的資料的指標給操作函式,就可以進行位操作了:

unsigned long word = 0;

set_bit(0, &word); /*第0位被設定*/

set_bit(1, &word); /*第1位被設定*/

clear_bit(1, &word); /*第1位被清空*/

change_bit(0, &word); /*翻轉第0位*/

為什麼關注原子操作?

1)在確認乙個操作是原子的情況下,多執行緒環境裡面,我們可以避免僅僅為保護這個操作在外圍加上效能開銷昂貴的鎖。

2)借助於原子操作,我們可以實現互斥鎖。

3)借助於互斥鎖,我們可以把一些列操作變為原子操作。

gnu c中x++是原子操作嗎?

答案不是。x++由3條指令完成。x++在單cpu下不是原子操作。

對應3條彙編指令

movl x, %eax

addl $1, %eax

movl %eax, x

在vc2005下對應

++x;

004232fa mov eax,dword ptr [x]

004232fd add eax,1

00423300 mov dword ptr [x],eax

仍然是3條指令。

所以++x,x++等都不是原子操作。因其步驟包括了從記憶體中取x值放入暫存器,加暫存器,把值寫入記憶體三個指令。

如何實現x++的原子性?

在單處理器上,如果執行x++時,禁止多執行緒排程,就可以實現原子。因為單處理的多執行緒併發是偽併發。

在多處理器上,需要借助cpu提供的lock功能。鎖匯流排。讀取記憶體值,修改,寫回記憶體三步期間禁止別的cpu訪問匯流排。同時我估計使用lock指令鎖匯流排的時候,os也不會把當前執行緒排程走了。要是調走了,那就麻煩了。

在多處理器系統中存在潛在問題的原因是:

不使用lock指令字首鎖定匯流排的話,在一次記憶體訪問週期中有可能其他處理器會產生異常或中斷,而在異常處理中有可能會修改尚未寫入的位址,這樣當inc操作完成後會產生無效資料(覆蓋了前面的修改)。

spinlock 用於cpu同步, 它的實現是基於cpu鎖定資料匯流排的指令.

當某個cpu鎖住資料匯流排後, 它讀乙個記憶體單元(spinlock_t)來判斷這個spinlock 是否已經被別的cpu鎖住. 如果否, 它寫進乙個特定值, 表示鎖定成功, 然後返回. 如果是, 它會重複以上操作直到成功, 或者spin次數超過乙個設定值. 鎖定資料匯流排的指令只能保證乙個機器指令內, cpu獨佔資料匯流排.

單cpu當然能用spinlock, 但實現上無需鎖定資料匯流排.

spinlock在鎖定的時候,如果不成功,不會睡眠,會持續的嘗試,單cpu的時候spinlock會讓其它process動不了.

多CPU的機器上在驅動中如何HOOK中斷

bool initmulticpuinformation 9u0wp95 i dword dwpcraddr dword dwcpunumbers 1 nffjls byte address byte halinitializeprocessor 7 l o dword i,len 128 w,j ...

發乙個多CPU中程序與CPU繫結的例子

現在多cpu的趨勢越來越大了.有時候為了更好地操作機器,需要將某個程序繫結到具體的cpu上去.下面給出了乙個程序繫結到具體的cpu上去的乙個例子.下面是在兩個終端分別執行了.cpu 0 cpu 2 後得到的結果.效果比較明顯.cpu0 5.3 us,5.3 sy,0.0 ni,87.4 id,0.0...

用Oracle並行查詢發揮多CPU的威力

在乙個單獨的伺服器中安裝更多的cpu成為目前的乙個趨勢。使用對稱多處理伺服器 smp 的情況下,乙個oracle伺服器擁有8個 16個或32個cpu以及幾吉位元ram的sga都不足為奇。oracle跟上了硬體發展的步伐,提供了很多面向多cpu的功能。從oracle8i開始,oracle在每個資料庫函...