少年阿賓

          那些青春的歲月

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks
          所謂原子操作,就是"不可中斷的一個或一系列操作" 。

          硬件級的原子操作:
          在單處理器系統(tǒng)(UniProcessor)中,能夠在單條指令中完成的操作都可以認為是" 原子操作",因為中斷只能發(fā)生于指令之間。這也是某些CPU指令系統(tǒng)中引入了test_and_set、test_and_clear等指令用于臨界資源互斥的原因。

          在對稱多處理器(Symmetric Multi-Processor)結構中就不同了,由于系統(tǒng)中有多個處理器在獨立地運行,即使能在單條指令中完成的操作也有可能受到干擾。

          在x86 平臺上,CPU提供了在指令執(zhí)行期間對總線加鎖的手段。CPU芯片上有一條引線#HLOCK pin,如果匯編語言的程序中在一條指令前面加上前綴"LOCK",經過匯編以后的機器代碼就使CPU在執(zhí)行這條指令的時候把#HLOCK pin的電位拉低,持續(xù)到這條指令結束時放開,從而把總線鎖住,這樣同一總線上別的CPU就暫時不能通過總線訪問內存了,保證了這條指令在多處理器環(huán)境中的

          原子性。
          軟件級的原子操作:
          軟件級的原子操作實現(xiàn)依賴于硬件原子操作的支持。
          對于linux而言,內核提供了兩組原子操作接口:一組是針對整數(shù)進行操作;另一組是針對單獨的位進行操作。
          2.1. 原子整數(shù)操作
          針對整數(shù)的原子操作只能對atomic_t類型的數(shù)據(jù)處理。這里沒有使用C語言的int類型,主要是因為:

          1) 讓原子函數(shù)只接受atomic_t類型操作數(shù),可以確保原子操作只與這種特殊類型數(shù)據(jù)一起使用

          2) 使用atomic_t類型確保編譯器不對相應的值進行訪問優(yōu)化

          3) 使用atomic_t類型可以屏蔽不同體系結構上的數(shù)據(jù)類型的差異。盡管Linux支持的所有機器上的整型數(shù)據(jù)都是32位,但是使用atomic_t的代碼只能將該類型的數(shù)據(jù)當作24位來使用。這個限制完全是因為在SPARC體系結構上,原子操作的實現(xiàn)不同于其它體系結構:32位int類型的低8位嵌入了一個鎖,因為SPARC體系結構對原子操作缺乏指令級的支持,所以只能利用該鎖來避免對原子類型數(shù)據(jù)的并發(fā)訪問。

          原子整數(shù)操作最常見的用途就是實現(xiàn)計數(shù)器。原子整數(shù)操作列表在中定義。原子操作通常是內斂函數(shù),往往通過內嵌匯編指令來實現(xiàn)。如果某個函數(shù)本來就是原子的,那么它往往會被定義成一個宏。

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

          atomic_t use_cnt;

          atomic_set(&use_cnt, 2);

          atomic_add(4, &use_cnt);

          atomic_inc(use_cnt);

          2.2. 原子性與順序性

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

          2.3. 原子位操作

          原子位操作定義在文件中。令人感到奇怪的是位操作函數(shù)是對普通的內存地址進行操作的。原子位操作在多數(shù)情況下是對一個字長的內存訪問,因而位號該位于0-31之間(在64位機器上是0-63之間),但是對位號的范圍沒有限制。

          編寫內核代碼,只要把指向了你希望的數(shù)據(jù)的指針給操作函數(shù),就可以進行位操作了:

          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)在確認一個操作是原子的情況下,多線程環(huán)境里面,我們可以避免僅僅為保護這個操作在外圍加上性能開銷昂貴的鎖。
          2)借助于原子操作,我們可以實現(xiàn)互斥鎖。
          3)借助于互斥鎖,我們可以把一些列操作變?yōu)樵硬僮鳌?/span>

          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值放入寄存器,加寄存器,把值寫入內存三個指令。

          如何實現(xiàn)x++的原子性?
          在單處理器上,如果執(zhí)行x++時,禁止多線程調度,就可以實現(xiàn)原子。因為單處理的多線程并發(fā)是偽并發(fā)。
          在多處理器上,需要借助cpu提供的Lock功能。鎖總線。讀取內存值,修改,寫回內存三步期間禁止別的CPU訪問總線。同時我估計使用Lock指令鎖總線的時候,OS也不會把當前線程調度走了。要是調走了,那就麻煩了。

          在多處理器系統(tǒng)中存在潛在問題的原因是:
          不使用LOCK指令前綴鎖定總線的話,在一次內存訪問周期中有可能其他處理器會產生異常或中斷,而在異常處理中有可能會修改尚未寫入的地址,這樣當INC操作完成后會產生無效數(shù)據(jù)(覆蓋了前面的修改)。

          spinlock 用于CPU同步, 它的實現(xiàn)是基于CPU鎖定數(shù)據(jù)總線的指令.
          當某個CPU鎖住數(shù)據(jù)總線后, 它讀一個內存單元(spinlock_t)來判斷這個spinlock 是否已經被別的CPU鎖住. 如果否, 它寫進一個特定值, 表示鎖定成功, 然后返回. 如果是, 它會重復以上操作直到成功, 或者spin次數(shù)超過一個設定值. 鎖定數(shù)據(jù)總線的指令只能保證一個機器指令內, CPU獨占數(shù)據(jù)總線.
          單CPU當然能用spinlock, 但實現(xiàn)上無需鎖定數(shù)據(jù)總線.

          spinlock在鎖定的時候,如果不成功,不會睡眠,會持續(xù)的嘗試,單cpu的時候spinlock會讓其它process動不了.
          posted on 2015-04-20 14:02 abin 閱讀(377) 評論(0)  編輯  收藏 所屬分類: concurrent
          主站蜘蛛池模板: 华坪县| 务川| 凌海市| 和田市| 阿图什市| 荆门市| 四子王旗| 贵阳市| 和静县| 全南县| 东光县| 黄山市| 岗巴县| 墨江| 滦南县| 大丰市| 东阳市| 六安市| 延津县| 巢湖市| 乌拉特前旗| 方城县| 文水县| 宽甸| 乐都县| 内乡县| 张家港市| 长春市| 陵川县| 五华县| 昌图县| 乐山市| 宁都县| 福泉市| 苍南县| 武穴市| 长寿区| 贵南县| 成安县| 张家港市| 海丰县|