使用者空間的同步(mutex, semaphore, futex)建立在「線程可被搶佔」的基礎上。但核心空間的同步複雜得多:除了進程上下文的搶佔,還有硬中斷、軟中斷、NMI、多核並行等多重並發源。不理解核心任務調度與執行上下文,就無法正確選擇自旋鎖、互斥鎖、RCU、完成量、信號量、原子變數等同步原語。本文結合核心任務調度體系,深度剖析核心同步設計哲學與實戰選型。
kworker 線程),可睡眠,適合耗時處理。理解「誰在跑、能不能睡、能不能被打斷」是選鎖的前提:
| 任務類型 | 上下文 | 可睡眠 | 可被搶佔 | 典型同步原語 |
|---|---|---|---|---|
| 硬中斷處理程式 | 中斷上下文 | 否 | 否(關中斷) | 自旋鎖 + local_irq_save/restore |
| 軟中斷 / Tasklet | 中斷上下文 | 否 | 否 | 自旋鎖 / 原子變數 |
| Workqueue (kworker) | 進程上下文 | 是 | 是 | Mutex / Semaphore / Completion |
| 系統呼叫 / 核心線程 | 進程上下文 | 是 | 是(可搶佔核心) | Mutex / Semaphore / RCU |
spin_lock_irqsave/restore(關本地中斷)、spin_lock_bh(關底半部)、raw_spinlock(不檢查搶佈/中斷,極度底層)。kmalloc(GFP_KERNEL), mutex_lock, copy_to_user, schedule() 等)。mutex_lock_interruptible)、除錯支援完善(CONFIG_DEBUG_MUTEXES)。struct semaphore) 同樣不可在中斷上下文使用,僅限進程上下文。rcu_read_lock()/unlock()(讀端)、synchronize_rcu() / call_rcu()(寫端同步/非同步回收)。init_completion(), wait_for_completion(), complete()。set_bit(), test_and_set_bit() 實現無鎖標誌位操作。spin_lock_irqsave。lockdep 靜態/動態檢測。spin_lock_irqsave 保護,否則死鎖隱患極大。synchronize_rcu() 會睡眠,不可在中斷/自旋鎖中呼叫;回調函式 call_rcu() 在軟中斷執行,同樣不可睡眠。核心同步的本質是「在正確的上下文,用正確的原語,保護正確的資料,持有最短的時間」。沒有萬能鎖,只有對上下文、調度、並發源頭的深度理解,才能在複雜的 SMP、可搶佔核心中寫出正確、高效、可維護的同步代碼。建議配合 lockdep, lockstat, ftrace 等工具,在開發測試階段徹底暴露同步缺陷。