Linux RCU佇列(2)樹形RCU佇列實現

2021-06-27 09:11:56 字數 2515 閱讀 5963

因為只用乙個全域性鎖在多處理器系統中會造成很嚴重的鎖競爭問題,一種有效的途徑是將鎖競爭關係變為一種層次關係(如圖1)。圖中四個rcu_node結構體都有自己的鎖,因此處理器0和處理器1之間將會先競爭底層的鎖然後在競爭高層的鎖,其他處理器也一樣。在grace period期間,只有獲取了底層的鎖的處理器才有機會獲取高層的鎖,也就是說只有每對處理器中的最後乙個才能夠記錄相應的grace period轉入到qs(quiescentstate)。這樣在高層鎖的競爭只有一半,而底層鎖的競爭只有1/3。

從圖一可以看到,rcu是嵌入到rcu_state結構體中的,不過從圖中只能看出這個結構體存在層次結構,rcu_node實際並不是以樹形結構儲存的,而是以佇列形式儲存(因為這是乙個滿樹,所以可以以佇列方式儲存在rcu_state中)。每乙個rcu_node包含乙個本節點所能夠保護的處理器範圍,最底層的rcu_node為null表明無父親節點。rcu_node佇列是以靜態方式在編譯時確定的,佇列的大小是由nr_cpus決定的。

下面以一**解釋grace period的發現過程,在第一幅圖中,沒有處理器進入到qs(紅色框表示沒有處理器進入到qs狀態)。假定所有的處理器同時請求進入到qs,那麼根據層次rcu的實現,每一組處理器中只有乙個能夠獲取到rcu_node中相應的鎖,因此在第二幅圖中僅僅只有三個綠色的框表明處理器0、3、5獲取到rcu_node的鎖。獲取到鎖的處理器將進行一些操作,但是很不幸的是他們還不是他們這一組中最後獲取鎖的處理器,所以只能夠退出而不能申請更高層的鎖。第三幅圖中,表明底層rcu_node中每一組的最後乙個處理器都申請到了鎖,因為是這一組中的最後乙個所以有資格申請更高層的鎖。後面三幅圖實際是前面三幅圖在高層的乙個迴圈,只有最後乙個獲取到高層鎖的處理器表明grace period結束。

上面可能並不能很明顯的看到鎖競爭所帶來的弊端,但是考慮到linux用在伺服器上,那麼層次上的結構對鎖競爭的影響就會有很大的提公升。

將上面的結構體組合到rcu或者rcu_bh中如下圖所示:

其中rcu_data結構體儲存的是每個處理其相關的資料,代表的是之前圖中所示的處理器。

不過有這些還不夠,因為還有乙個問題沒有解決,當多處理器中僅僅很少一部分再進行運算而很大一部分處理器處於休眠狀態,那麼頻繁的rcu更新會打斷處理器的休眠。休眠狀態有兩種,第一種是當預處理器沒有任務進入到完全的休眠狀態,還有一種是當預處理器上的任務在等待從而進入到休眠狀態。前者沒有問題,而後者則很危險。假設處理器處於rcu的讀者部分的互斥區中,而這部分**需要大量的時間才能夠完成,期間需要等待資料完成從而進入到休眠狀態。而喚醒休眠是通過中斷進行的。而在rcu讀者互斥區是不能被中斷的,所以就導致grace period永遠也不會結束。

因此需要增加新的資訊來儲存當預處理器的狀態,整體結構變成下圖所示。其中rcu_dynticks表明當預處理器休眠時是否處於rcu讀者部分互斥區。rcu_dynticks有乙個計數成員變數,當這個成員變數時偶數時表明處理器沒有在rcu讀者部分互斥區中,反之處理器在rcu讀者部分互斥區。

只有當rcu_dynticks結構體中的計數為奇數時rcu只需要等待qs,而不需要喚醒真正處於睡眠狀態下的處理器。

根據上面的狀態可以看出整個rcu狀態的切換好像乙個狀態機機制變換,具體變換流程如下圖所示:

在乙個比較繁忙的系統上的狀態轉換機將會集中在最上層的兩個迴圈中——在每個grace period的起始部分進行初始化,然後等待qs,並且會注意到處理器轉入到qs。在這樣的系統上,qs將會發生在每一次系統上下文切換,對於空閒處理器或者處於使用者記憶體空間**的則是時鐘中斷。處理器熱插拔時間將會將狀態機帶入到離線cpu狀態。當存在被拔出的處理器沒有能夠快速的轉入到qs將會執行 「傳送重排程ipi給已經拔出的處理器」來保持通路的連通性。rcu的實現通過標誌這些處理器正在擴充套件qs來避免不必要的空閒處理器喚醒。只有拔出的處理器才需要經過擴充套件qs時需要傳送重排程,真正的空閒處理器直接通過上面的dynticks結構體中的計數直接跳過。當處理器在傳送重排程之後grace period仍然超時,並且核心配置了config_rcu_cpu_stall_detector則會傳送超時事件給系統進行診斷。

下面是整個狀態機變換的時間順序圖:

不過上訴狀態機切換並不能直接轉換為c**,取而代之的是這些**唄作為乙個實現在事件驅動的核心。

Play Framework發布1 1RC2版本

來自 url play framework於當地時間2010 10 17日發布了play framework 1.1 rc2版本,這距離上次1.1rc1版本僅僅相差12天。本次發布似乎沒有發布release notes,也就是沒有說明做了哪些更新。從play framework的發布情況看,今年pl...

樹形dp小結 2

樹形dp的題一般都結合著揹包來用。以下的幾道題都是結合著揹包的思想來的 1 樹形dp 分組揹包 狀態比較難想 之前說過在最長距離的那道題裡說過,不會返回。但是有的提示需要考慮返回的節點的。下面就是乙個例子 題目大意 給你乙個蘋果樹,有n個節點,每個節點上都有乙個乙個蘋果也就有乙個權值,當你經過這個點...

佇列2 迴圈佇列

我們來看下上次我們寫下的queue struct queue 增加乙個 void pop 彈出第乙個 bool empty 是否為空 int num 返回有多少個元素 int gethead 獲取第乙個元素 現在我們增加乙個功能,獲取第i個元素。int getnum int x 好的,開始在裡邊填充...