天天看點

Linux核心連結清單 記憶體屏障,從cache一緻性到了解記憶體屏障

本帖最後由 blake326 于 2013-03-07 00:12 編輯

http://bbs.chinaunix.net/forum.p ... =4070325&extra=

http://bbs.chinaunix.net/forum.p ... ;page=6#pid23786513

參考上面兩個文章。純屬個人總結了解,具體實作應該更複雜或者以此為基礎。

先說cache 一緻性的了解。

假設有cpu0, cpu1。一個虛拟位址addr。

則cpu0, cpu1都有一個addr對應的cache line, 狀态有四種:有效E, 修改M,共享S, 無效I。I實際上就是說該addr在cache中沒有緩存的意思。根據這四種狀态,組合分析一下。

cpu0,cpu1的cache狀态一樣:

都是I狀态:

這種最簡單,cpu0讀addr的話,則配置設定一個cache line并且從ddr把addr的值讀取過來,狀态變成E。cpu0寫的話,狀态會變成M。

都是S狀态:

也很簡單,讀的話直接從cache讀出來。寫的話複雜一點,cpu0寫addr,修改本地cache,狀态變成M。并且發送一個inv消息給cpu1通知cpu1失效響應的cache line。

都是E,M的狀态是不存在的。

cpu0,cpu1的cache狀态不一樣:

cpu0 I,  cpu1 E:

cpu0 讀addr,發現cpu1有對應的E cache,建立一個本地的cache line将cpu1的cache line拷貝過來,并且設定大家的狀态為S。

cpu0 寫addr,發現cpu1有對應的E cache,發送一個inv消息告訴cpu1,然後本地建立一個cache line将ddr讀進來,之後修改本地cache line,并且設定狀态M。

cpu1 讀addr,直接讀cache。

cpu1 寫addr,直接寫cache,設定狀态為M。

cpu0 I, cpu1 M:

cpu0 讀addr,發現cpu1有對應的M cache,發送一個flush消息告訴cpu1并且等待cpu1重新整理cache到ddr,然後建立一個本地cache line将ddr從記憶體讀進來,并且設定狀态為E。

cpu0 寫addr,發現cpu1有對應的M cache,發送一個flush消息告訴cpu1并且等待cpu1重新整理cache到ddr,然後建立一個本地cache line将ddr從記憶體讀進來并且寫,并且設定狀态為M。

cpu1 讀addr,直接讀。

cpu1 寫addr,直接寫。

記憶體屏障,一個經典的場景,插傳入連結表一個節點。

cpu0

new->next = next;

wmb();

prev->next = new;

cpu1

讀 prev->next

rmb();

讀 new->next

如果沒有記憶體屏障,cpu1讀到最新的prev->next的時候,可能還沒有讀到最新的new->next。核心就要挂了。

換一種舒服的例子:

cpu0

a = 1;

wmb();

b = 2;

cpu1

讀 b;

rmb();

讀 a;

通過記憶體屏障能夠保證cpu1假設讀取到了b=2,那麼cpu1讀取的a肯定等于1。

看一看實作:

假設現在a和b在不同的cache line,并且不管在哪個cpu,他們的狀态都是S的。(其他狀态可以一樣的分析)

cpu0三條指令,通過wmb()保證 先發送inv a,在發送inv b。或者說是,cpu1先收到inv a,後收到inv b的消息。

cpu1三條指令,rmb()保證在執行後面的指令之前,rmb前面所有的inv 指令都必須執行完成。假設讀b的時候,inv b已經收到并且已經執行,則b擷取到了最新的值。那麼在執行讀a的指令之前,一定會吧inv a執行了,因為inv a在inv 本就收到了(這個是cpu0的wmb保證的)

是以wmb,rmb都是缺一不可的。

補充改一下L2 cache,arm架構經常會有l2 cache, 不同與L1cahce, L2 cache是smp共享的一個cache。上面說的cache重新整理到ddr,從ddr讀取到cache,其實都是經過這個L2 cache的,比如cache重新整理ddr,如果l2cache也有對應的cache的話則這個cache知會重新整理到l2 cache, l2 cache會等待适當的時機或者主動調用api被重新整理到真正的ddr。讀的話也類似。

一般來說,L2 cache對軟體來說是很透明的,除了啟動的時候要初始化一下,并且需要将cache重新整理真正的ddr的時候可以調用響應的操作方法來重新整理。比如dma操作之前,可能需要clean或者inv L2 cache。