**隨著多核時代的到來,併發操作已經成了很正常的現象,作業系統必須要有一些機制和原語,以保證某些基本操作的原子性,比如處理器需要保證讀乙個位元組或寫乙個位元組是原子的,那麼它是如何實現的呢?有兩種機制:匯流排鎖定和快取一致性。
我們知道,cpu和物理記憶體之間的通訊速度遠慢於cpu的處理速度,所以cpu有自己的內部快取,根據一些規則將記憶體中的資料讀取到內部快取中來,以加快頻繁讀取的速度。我們假設在一台pc上只有乙個cpu和乙份內部快取,那麼所有程序和執行緒看到的數都是快取裡的數,不會存在問題;但現在伺服器通常是多 cpu,更普遍的是,每塊cpu裡有多個核心,而每個核心都維護了自己的快取,那麼這時候多執行緒併發就會存在快取不一致性,這會導致嚴重問題。
以 i++為例,i的初始值是0.那麼在開始每塊快取都儲存了i的值0,當第一塊核心做i++的時候,其快取中的值變成了1,即使馬上回寫到主記憶體,那麼在回寫之後第二塊核心快取中的i值依然是0,其執行i++,回寫到記憶體就會覆蓋第一塊核心的操作,使得最終的結果是1,而不是預期中的2.
那麼怎麼解決整個問題呢?作業系統提供了匯流排鎖定的機制。前端匯流排(也叫cpu匯流排)是所有cpu與晶元組連線的主幹道,負責cpu與外界所有部件的通訊,包括快取記憶體、記憶體、北橋,其控制匯流排向各個部件傳送控制訊號、通過位址匯流排傳送位址訊號指定其要訪問的部件、通過資料匯流排雙向傳輸。在cpu1要做 i++操作的時候,其在匯流排上發出乙個lock#訊號,其他處理器就不能操作快取了該共享變數記憶體位址的快取,也就是阻塞了其他cpu,使該處理器可以獨享此共享記憶體。
但我們只需要對此共享變數的操作是原子就可以了,而匯流排鎖定把cpu和記憶體的通訊給鎖住了,使得在鎖定期間,其他處理器不能操作其他記憶體位址的資料,從而開銷較大,所以後來的cpu都提供了快取一致性機制,intel的奔騰486之後就提供了這種優化。
快取一致性機制整體來說,是當某塊cpu對快取中的資料進行操作了之後,就通知其他cpu放棄儲存在它們內部的快取,或者從主記憶體中重新讀取,如下圖:
這裡以在intel系列中廣泛使用的mesi協議詳細闡述下其原理。
mesi 協議是以快取行(快取的基本資料單位,在intel的cpu上一般是64位元組)的幾個狀態來命名的(全名是modified、exclusive、 share or invalid)。該協議要求在每個快取行上維護兩個狀態位,使得每個資料單位可能處於m、e、s和i這四種狀態之一,各種狀態含義如下:
m:被修改的。處於這一狀態的資料,只在本cpu中有快取資料,而其他cpu中沒有。同時其狀態相對於記憶體中的值來說,是已經被修改的,且沒有更新到記憶體中。
e:獨佔的。處於這一狀態的資料,只有在本cpu中有快取,且其資料沒有修改,即與記憶體中一致。
s:共享的。處於這一狀態的資料在多個cpu中都有快取,且與記憶體一致。
i:無效的。本cpu中的這份快取已經無效。
這裡首先介紹該協議約定的快取上對應的監聽:
乙個處於m狀態的快取行,必須時刻監聽所有試圖讀取該快取行對應的主存位址的操作,如果監聽到,則必須在此操作執行前把其快取行中的資料寫回主存。
乙個處於s狀態的快取行,必須時刻監聽使該快取行無效或者獨享該快取行的請求,如果監聽到,則必須把其快取行狀態設定為i。
乙個處於e狀態的快取行,必須時刻監聽其他試圖讀取該快取行對應的主存位址的操作,如果監聽到,則必須把其快取行狀態設定為s。
當cpu需要讀取資料時,如果其快取行的狀態是i的,則需要從記憶體中讀取,並把自己狀態變成s,如果不是i,則可以直接讀取快取中的值,但在此之前,必須要等待其他cpu的監聽結果,如其他cpu也有該資料的快取且狀態是m,則需要等待其把快取更新到記憶體之後,再讀取。
當cpu需要寫資料時,只有在其快取行是m或者e的時候才能執行,否則需要發出特殊的rfo指令(read or ownership,這是一種匯流排事務),通知其他cpu置快取無效(i),這種情況下會效能開銷是相對較大的。在寫入完成後,修改其快取狀態為m。
所以如果乙個變數在某段時間只被乙個執行緒頻繁地修改,則使用其內部快取就完全可以辦到,不涉及到匯流排事務,如果快取一會被這個cpu獨佔、一會被那個cpu 獨佔,這時才會不斷產生rfo指令影響到併發效能。這裡說的快取頻繁被獨佔並不是指執行緒越多越容易觸發,而是這裡的cpu協調機制,這有點類似於有時多執行緒並不一定提高效率,原因是執行緒掛起、排程的開銷比執行任務的開銷還要大,這裡的多cpu也是一樣,如果在cpu間排程不合理,也會形成rfo指令的開銷比任務開銷還要大。當然,這不是程式設計者需要考慮的事,作業系統會有相應的記憶體位址的相關判斷,這不在本文的討論範圍之內。
並非所有情況都會使用快取一致性的,如被操作的資料不能被快取在cpu內部或運算元據跨越多個快取行(狀態無法標識),則處理器會呼叫匯流排鎖定;另外當cpu不支援快取鎖定時,自然也只能用匯流排鎖定了,比如說奔騰486以及更老的cpu。
自旋鎖和快取一致性
class taslock void unlock atomicboolean 的getandset 方法是原子的,tas迴圈執行 getandset 方法,當發現原來的值為 false 且設定新值為 true 成功後,則認為獲取到了鎖。class ttaslock if state.getands...
一致性非鎖定讀和一致性鎖定讀
在預設配置下innodb的隔離級別是repeatable read,innodb的select操作使用的是一致性非鎖定讀 一致性的非鎖定行讀 consistent nonlocking read,簡稱cnr 是指innodb儲存引擎通過行多版本控制 multi versioning 的方式來讀取當前...
一致性非鎖定讀與一致性鎖定讀
一致性非鎖定讀 consistent nonlocking read 是指innodb儲存引擎通過 行多版本控制 multi versioning 的方式來讀取當前執行時間資料庫中行的資料。如果讀取的行正在執行delete或update操作,這時讀取操作不會因此去等待行上鎖的釋放,相反地,innod...