在Linux核心中存在數種上鎖機制,針對不同情況,我們需要清楚知道使用何種方式才能夠使得核心行為正常並且有效率。許多講解Linux device driver以及核心的書籍或文章都已經寫得很好了,這篇blog主要是為了自己方便所作的整理。
以driver撰寫者的角度,以下三種方式是最常用的:
以driver撰寫者的角度,以下三種方式是最常用的:
- atomic_xxx():適用於保護簡單的整數型別計數。
- spin_lock/spin_unlock:在SMP的環境中,以busy waiting的方式上鎖,在UP下是空運算,CONFIG_PREEMPT開啟時,則還會進行preempt_disable()/preempt_enable()。適合用於保護短時間(少於幾個tick)的臨界區。driver撰寫者常常會使用其變形 - spin_lock_irqsave()/spin_unlock_restore(),來避免被ISR裡的程式碼中斷。在臨界區裡不能進入睡眠。
- mutex:binary semaphore。適用於保護在process context下的較長臨界區存取。當無法獲取鎖時,會進入睡眠狀態。當然,一般無法在ISR中使用,如果實在需要在ISR中獲得,必須以mutex_trylock()獲得,但要作好無法獲得lock時的處理。
如果要追蹤核心或撰寫subsystem,還需注意其他可用機制:
- RCU(Read-Copy-Update):共用資源大部份唯讀,少數寫入的情況。臨界區內不能睡。受保護的資源必須以pointer存取。RCU的實作者寫了不少文章介紹其原理與設計,可以參考這邊。
- rwlock:任意數目process的讀,但只能一個寫。有semaphore與spin_lock的版本。必須確認寫只有一個。jiffies就是一例。
- rt_mutex:必須開啟CONFIG_RT_MUTEX。用來減緩priority inversion的延遲程度(採用priority inheritance的策略)。不過目前只有kernel/下有用到。原理與實作可參考LWN上的好文章。
使用適當的上鎖機制不只是為了行為正確,也是為了讓lock contention的程度不會嚴重影響效能。然而,要降低lock contention,往往意味著用較細的粒度去上鎖,但較細的粒度表示lock的數量會增加,使得上鎖的順序必須一致以避免dead lock,但這會使得程式碼邏輯顯得複雜。實務上,必須將要求與影響考慮在內,然後取得一個最佳選擇。
留言
張貼留言