一、背景
linux系統里有非常多的鎖種類,除了spinlock,mutex,rwlock,rwsem,還有rcu及順序鎖,這里面還有不少鎖變種,比如spinlock的帶bh或者irq字樣的lock/unlock,還有nmi里可以用的順序鎖,還有不同模式的rcu等等,可見linux里的鎖或者說內核同步原語的細節是非常非常多的。
普通linux里已經有這么多的細節了,但是如果加上rt-linux的patch,鎖的細節就更加多了,尤其是rt-linux里與提高實時性相關的重要的鎖改動,也就是以rtmutex為核心的那些鎖的改動。
在之前的博客?rcu的實例、注意事項及原理講解_rcu使用實例-CSDN博客 里,我們已經詳細介紹了rcu,另外,在之前的博客?順序鎖的原理和使用注意事項-CSDN博客 里,我們也詳細介紹了順序鎖,這篇博客里就不涉及這兩個鎖了。
這篇博客里,我們基于rt-linux這個大前提,來分析rt-linux里所有用到rtmutex核心的那些鎖的調用鏈情況,從而能整體上對rt-linux里的這些rtmutex的衍生品鎖,所謂的泛rtmutex鎖有一個整體的認知。
二、泛rtmutex鎖的調用鏈完整圖及整體介紹
2.1?泛rtmutex鎖的調用鏈完整圖
我有整理一個泛rtmutex鎖的調用鏈完整圖,在資源?https://download.csdn.net/download/weixin_42766184/90894722 里。
完整的圖非常非常大,縮小以后完全看不清:
2.2 整體介紹
rt-linux系統為了提高整個系統的實時性,修改了spinlock的實現,除了raw_spin_lock_xx保持原來的實現之外,其他的不帶raw的spinlock都改成了rtmutex為核心的實現。不帶raw的spinlock里不再像普通linux里一直死鎖樂觀地一等到底,而是等一段時間后,變為了rtmutex的等待方式,交出cpu,這樣做肯定會讓系統的吞吐量下降,因為上下文切換變多了,但是好處就是讓鎖的邏輯里也增加了調度點,讓系統里更高優先級的任務能更早地進行響應。
這篇博客并不會深入到具體某個鎖的實現細節里去,主要看的是rt-linux下rtmutex核心的這些鎖的調用鏈關系,看里面哪些邏輯是公用的,是如何使用rtmutex核心的邏輯,各個鎖之間的調用鏈關系上。
在第三章里的分類介紹之前,我們對整理出的圖里的共性內容做一些簡要說明。
2.2.1?CONFIG_DEBUG_LOCK_ALLOC和CONFIG_LOCKDEP
可以從圖里看到,mutex系列、rt_mutex系列、down_read/up_read/down_write/up_write系列、rt讀寫鎖系列、讀寫鎖系列、rt自旋鎖系列、自旋鎖系列都有關于CONFIG_DEBUG_LOCK_ALLOC編譯選項開和不開的一些分叉,在開了CONFIG_DEBUG_LOCK_ALLOC時,會多出一些xx_nested函數和宏,這些是用于調試lock的,用于檢查內核是否錯誤地釋放被持有的鎖,什么意思呢?就是檢測使用中的鎖是否被意外釋放,或者使用中的鎖被重新初始化,或者在進程退出時有持有鎖。另外,圖里還有一些與CONFIG_LOCKDEP編譯選項有關的宏或者函數的定義分叉,CONFIG_LOCKDEP是整個lockdep的總開關,更多的關于lockdep的編譯選項的描述就不在這里展開了。
2.2.2 rtmutex核心邏輯函數
另外一點重點需要說明的是,圖里列出來的這些鎖,無論是哪個系列的鎖,最終都會或多或少的使用到rtmutex核心邏輯的一些實現的接口函數,所以rtmutex核心邏輯的實現的接口函數還是非常多的,其實現在rtmutex.c里,在圖里并沒有列全,列了一部分,為的是有一個直觀的認知,是最終用到了rtmutex的核心函數,如下:
這塊rtmutex的核心邏輯函數主要是處理一些與優先級繼承相關的一些功能,用于防止優先級反轉等影響系統實時性而加的一些邏輯。
我們以__rt_mutex_slowlock來做一個展開,這個函數里的實現大致有下圖里的這些內容:
可以看到,__rt_mutex_slowlock的實現里的這些函數大部分都是被其他函數如__rwbase_read_unlock,rt_mutex_slow_unlock、rt_mutex_slowunlock等函數所復用的。
2.2.3 rwbase_rt.c里被同時兩個.c包含呈現兩種不同的實現形態
rwbase_rt.c以及使用rwbase_rt.c的rwsem.c和spinlock_rt.c是一個有點類似C++基類繼承的關系,就是說rwbase_rt.c是實現了一個基類,rwbase_rt.c的這個基類的實現里用到了一些純虛接口,而這些用到的純虛接口是在rwsem.c和spinlock_rt.c里各自定義自己的實現的。
這其實就是linux內核里,用C來模擬出C++的繼承的方式,進行的一種抽象的實現,和內核里大量存在ops函數的用法不一樣,但是核心的思想是一樣的。
下圖就是圖里的上面說的rwsem.c和spinlock_rt.c里同一個函數rwbase_rtmutex_slowlock_locked的不同的呈現形態(實際有了不同的實現):
三、分類介紹
我們按照圖里的從上到下的順序進行依次介紹。
3.1 mutex系列和rt_mutex系列
mutex系列指的是調用的函數是mutex_開頭的這些鎖,如下圖:
?rt_mutex系列指的是調用的函數是ww_mutex_開頭的這些鎖,如下圖:
在rt-linux系統上,mutex系列和rt_mutex系列雖然在函數定義上來說并沒有馬上畫等號的指代,但是,很快就邏輯上合并了,如下圖,mutex_xx系列用到的__mutex_lock_common繼而就馬上用到了__rt_mutex_lock,而rt_mutex_xx系列用到的_rt_mutex_lock_common繼而馬上也用到了__rt_mutex_lock:
對于mutex_trylock直接用的就是__rt_mutex_trylock來實現的。
3.2 ww_mutex系列
ww_mutex系列指的是調用的函數是ww_mutex_開頭的這些鎖,如下圖:
ww_mutex系列是用于處理AB-BA死鎖情形的一個鎖,但是這種處理其實也只是說是為了避免系統嚴重的死鎖崩潰,即,當檢測到發生死鎖時,讓其中一個鎖先unlock。
ww-mutex里ww表示wound-wait,是受傷地等待的意思,一方受傷地等待比讓系統直接崩潰看起來會好一些,但是這可能也隱藏了問題,當前系統里使用ww_mutex系列的鎖的人并不多。
需要說明的是ww_mutex系列的鎖最終用到的還是rt_mutex_slowlock這樣的rtmutex核心邏輯函數來實現的。
3.3?down_read/up_read/down_write/up_write系列
down_read/up_read/down_write/up_write系列指的是調用的函數是down_read_開頭和up_read/down_write_開頭/up_write的這些鎖,如下圖:
down_read/up_read/down_write/up_write系列其實也就是rwsem系列,rwsem和讀寫spinlock鎖都是有讀和寫兩個角色的,都是為了照顧多讀少寫場景下的并發性能,前者rwsem是會睡眠的,這是很顯然的,后者讀寫spinlock鎖在普通linux下是樂觀地死等的,而在rt-linux下,則是會進行睡眠的。
回到這里說的rwsem,這個down_read/up_read/down_write/up_write系列的核心實現在rwsem.c里,而rwsem.c剛 2.2.3 里也講到,是用到了rwbase_rt.c里的“基類”實現。
3.4?rt讀寫鎖系列和讀寫鎖系列
rt讀寫鎖系列指的是調用的函數是rt_write_開頭和rt_read_開頭的這些鎖,如下圖:
讀寫鎖系列指的是調用的函數是write_開頭和read_開頭的這些鎖,如下圖:
rt-linux里rt讀寫鎖和讀寫鎖在實現是很快就合并到用rt_write_xx和rt_read_xx這些rt_開頭的接口里了:
rt_write_xx和rt_read_xx的具體實現,是在spinlock_rt.c里,而spinlock_rt.c剛 2.2.3 里也講到,是用到了rwbase_rt.c里的“基類”實現。
3.5?rt自旋鎖系列和自旋鎖系列
rt自旋鎖系列指的是調用的函數是rt_spin_開頭的這些鎖,如下圖:
自旋鎖系列指的是調用的函數是spin_開頭的這些鎖,如下圖:
與 3.4 差不多,這里的 rt自旋鎖系列和自旋鎖系列很快也在實現上合并了,都是用的rt_spin_xx開頭的這些的實現,這些的實現里,用到了rt_mutex_slowtrylock、rtlock_slowlock等rtmutex里的核心實現函數,這些核心函數的使用在實現上和rt_mutex_系列接口相比,在使用rtmutex核心邏輯函數上,也是比較接近的。