您當前的位置:首頁 > 舞蹈

linux核心--自旋鎖的理解

作者:由 CPP加油站 發表于 舞蹈時間:2021-11-04

自旋鎖:如果核心配置為SMP系統,自旋鎖就按SMP系統上的要求來實現真正的自旋等待,但是對於UP系統,自旋鎖僅做搶佔和中斷操作,沒有實現真正的“自旋”。如果配置了CONFIG_DEBUG_SPINLOCK,那麼自旋鎖按照SMP系統來編譯。

但是為什麼在UP系統中不需要真正的“帶有自旋的”自旋鎖呢?其實在理解了自旋鎖的概念和由來,這個問題就迎刃而解了。所以我重新查找了關於自旋鎖的資料,認真研究了自旋鎖的實現和相關內容。

一、自旋鎖spinlock的由來

眾所周知,自旋鎖最初就是為了SMP系統設計的,實現在多處理器情況下保護臨界區。所以在SMP系統中,自旋鎖的實現是完整的本來面目。但是對於UP系統,自旋鎖可以說是SMP版本的閹割版。因為只有在SMP系統中的自旋鎖才需要真正“自旋”。

二、自旋鎖的目的

自旋鎖的實現是為了保護一段短小的臨界區操作程式碼,保證這個臨界區的操作是原子的,從而避免併發的競爭冒險。在Linux核心中,自旋鎖通常用於包含核心資料結構的操作,你可以看到在許多核心資料結構中都嵌入有spinlock,這些大部分就是用於保證它自身被操作的原子性,在操作這樣的結構體時都經歷這樣的過程:上鎖-操作-解鎖。

如果核心控制路徑發現自旋鎖“開著”(可以獲取),就獲取鎖並繼續自己的執行。相反,如果核心控制路徑發現鎖由執行在另一個CPU上的核心控制路徑“鎖著”,就在原地“旋轉”,反覆執行一條緊湊的迴圈檢測指令,直到鎖被釋放。 自旋鎖是迴圈檢測“忙等”,即等待時核心無事可做(除了浪費時間),程序在CPU上保持執行,所以它保護的臨界區必須小,且操作過程必須短。不過,自旋鎖通常非常方便,因為很多核心資源只鎖1毫秒的時間片段,所以等待自旋鎖的釋放不會消耗太多CPU的時間。

三、自旋鎖需要做的工作

從保證臨界區訪問原子性的目的來考慮,自旋鎖應該阻止在程式碼執行過程中出現的任何併發干擾。這些“干擾”包括:

1、中斷,包括硬體中斷和軟體中斷(僅在中斷程式碼可能訪問臨界區時需要)

這種干擾存在於任何系統中,一箇中斷的到來導致了中斷例程的執行,如果在中斷例程中訪問了臨界區,原子性就被打破了。所以如果在某種中斷例程中存在訪問某個臨界區的程式碼,那麼就必須用spinlock保護。對於不同的中斷型別(硬體中斷和軟體中斷)對應於不同版本的自旋鎖實現,其中包含了中斷禁用和開啟的程式碼。但是如果你保證沒有中斷程式碼會訪問臨界區,那麼使用不帶中斷禁用的自旋鎖API即可。

2、核心搶佔(僅存在於可搶佔核心中)

在2。6以後的核心中,支援核心搶佔,並且是可配置的。這使UP系統和SMP類似,會出現核心態下的併發。這種情況下進入臨界區就需要避免因搶佔造成的併發,所以解決的方法就是在加鎖時禁用搶佔(preempt_disable(); ),在開鎖時開啟搶佔(preempt_enable();注意此時會執行一次搶佔排程)。

3、其他處理器對同一臨界區的訪問(僅SMP系統)

在SMP系統中,多個物理處理器同時工作,導致可能有多個程序物理上的併發。這樣就需要在記憶體加一個標誌,每個需要進入臨界區的程式碼都必須檢查這個標誌,看是否有程序已經在這個臨界區中。這種情況下檢查標誌的程式碼也必須保證原子和快速,這就要求必須精細地實現,正常情況下每個構架都有自己的彙編實現方案,保證檢查的原子性。

有些人會以為自旋鎖的自旋檢測可以用for實現,這種想法“Too young, too simple, sometimes naive”!你可以在理論上用C去解釋,但是如果用for,起碼會有如下兩個問題:

(1)你如何保證在SMP下其他處理器不會同時訪問同一個的標誌呢?(也就是標誌的獨佔訪問)

(2)必須保證每個處理器都不會去讀取快取記憶體而是真正的記憶體中的標誌(可以實現,程式設計上可以用volitale) 要根本解決這個問題,需要在晶片底層實現物理上的記憶體地址獨佔訪問,並且在實現上使用特殊的彙編指令訪問。請看參考資料中對於自旋鎖的實現分析。以arm為例,從存在SMP的ARM構架指令集開始(V6、V7),採用LDREX和STREX指令實現真正的自旋等待。

四、自旋鎖操作組成

根據上的介紹,我們很容易知道自旋鎖的組成:

中斷控制(僅在中斷程式碼可能訪問臨界區時需要)

搶佔控制(僅存在於可搶佔核心中需要)

自旋鎖標誌控制 (僅SMP系統需要)

中斷控制是按程式碼訪問臨界區的不同而在程式設計時選用不同的變體,有些API中有,有些沒有。

而搶佔控制和自旋鎖標誌控制依據核心配置(是否支援核心搶佔)和硬體平臺(是否為SMP)的不同而在編譯時確定。如果不需要,相應的控制程式碼就編譯為空函式。 對於非搶佔式核心,由自旋鎖所保護的每個臨界區都有禁止核心搶佔的API,但是為空操作。由於UP系統不存在物理上的並行,所以可以閹割掉自旋的部分,剩下搶佔和中斷操作部分即可。

到這裡其實就可以解釋為什麼我開始的實驗現象和預想的完全不同了:

由於UP系統(在不配置CONFIG_DEBUG_SPINLOCK的情況下),根本就沒有自旋鎖控制的部分,多次獲得自旋鎖是可能的(這種程式設計本來就是錯誤的,只是我想看錯誤的現象而已)。

對於其中的一點疑惑:

1、在有禁用中斷的版本中,既然已經禁用了中斷,在本處理器上就不會被打斷,禁用搶佔是否多餘?

(1)禁用了中斷可以避免因為中斷引起的搶佔排程,但是如果在自旋鎖保護的臨界區中存在 preempt_disable();和 preempt_enable();對。這樣在preempt_enable();就會引發搶佔排程。

(2)避免SMP系統中別的處理器執行排程程式使得本處理器的程序會被排程出去。?????

對於這個問題我不是很確定,還有深入研究排程系統後才會有準確的答案。

【文章福利】:小編整理了一些個人覺得比較好的學習書籍、影片資料共享在群檔案裡面,有需要的可以自行新增哦!~點選加入(需要自取)

linux核心--自旋鎖的理解

linux核心--自旋鎖的理解

標簽: 自旋  核心  SMP  中斷  臨界