自旋鎖是 Linux 核心最基礎的同步機制,但其與調度器的互動細節常被誤解。本文系統對比自旋鎖與信號量、分析 preempt_disable() 機制、詳細解釋為何自旋鎖保護區不能睡眠、列舉核心搶佔發生時機,並總結單核/多核/搶佔式核心下自旋鎖的不同行為。
| 應用場景 | 推薦選擇 | 理由 |
|---|---|---|
| 低開銷、臨界區極短 | 自旋鎖 | 避免睡眠/喚醒開銷 |
| 臨界區較長 | 信號量 | 避免長時間自旋浪費 CPU |
| 臨界區可能睡眠 | 信號量 | 自旋鎖區睡眠會死鎖 |
| 非進程上下文(中斷) | 自旋鎖 | 中斷上下文不能睡眠 |
| 非阻塞嘗試 | 信號量 | down_trylock() |
#define spin_lock(lock) _spin_lock(lock)
#define _spin_lock(lock) __LOCK(lock)
#define __LOCK(lock) \
do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)
spin_lock 呼叫 preempt_disable() → 關閉核心搶佔spin_unlock 呼叫 preempt_enable() → 重新開啟搶佔schedule()、kmalloc(GFP_KERNEL)、copy_from_user())結論:自旋鎖保護區絕對不能包含可能導致睡眠的程式碼。
| 核心配置 | 自旋鎖行為 |
|---|---|
| 單核、非搶佔 | 編譯時完全優化移除(空操作) |
| 單核、搶佔式 | 僅作為 preempt_disable/enable 開關 |
| 多核 | 完整功能:防止多核並發訪問 + 防止搶佔競爭 |
spin_unlock 後)schedule()I/O 事件發生 → 中斷處理程式喚醒等待進程 → 設定 need_resched → 中斷返回觸發調度 → 等待進程獲得 CPU → 毫秒級響應,與時間片長度無關。