Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Linux 自旋鎖與核心調度關係深度解析:為什麼自旋鎖不能睡眠

編輯:Linux 教學

自旋鎖是 Linux 核心最基礎的同步機制,但其與調度器的互動細節常被誤解。本文系統對比自旋鎖與信號量、分析 preempt_disable() 機制、詳細解釋為何自旋鎖保護區不能睡眠、列舉核心搶佔發生時機,並總結單核/多核/搶佔式核心下自旋鎖的不同行為。

一、自旋鎖簡介

  • 同一時間僅一個核心任務持有
  • 適用場景:多核心系統、搶佔式核心
  • 獲取失敗時忙等待(自旋),不放棄 CPU

二、信號量簡介

  • 睡眠鎖:獲取失敗將任務加入等待佇列並睡眠
  • CPU 釋放執行其他程式碼
  • 持有者釋放時喚醒等待者

三、自旋鎖 vs 信號量 選擇指南

應用場景推薦選擇理由
低開銷、臨界區極短自旋鎖避免睡眠/喚醒開銷
臨界區較長信號量避免長時間自旋浪費 CPU
臨界區可能睡眠信號量自旋鎖區睡眠會死鎖
非進程上下文(中斷)自旋鎖中斷上下文不能睡眠
非阻塞嘗試信號量down_trylock()

四、核心關鍵:自旋鎖實作與 preempt_disable()


#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() → 重新開啟搶佔
  • 即使在自旋等待期間,搶佔也關閉

五、為什麼自旋鎖區不能睡眠?

  1. 假設自旋鎖區呼叫睡眠函數(如 schedule()、kmalloc(GFP_KERNEL)、copy_from_user())
  2. 觸發調度,切換到其他進程
  3. 新進程嘗試獲取同一自旋鎖 → 進入自旋等待
  4. 因搶佔關閉,當前 CPU 不會再發生調度
  5. 持有鎖的原進程已睡眠,無法釋放鎖
  6. 新進程永久自旋 → 死鎖

結論:自旋鎖保護區絕對不能包含可能導致睡眠的程式碼。

六、自旋鎖在不同核心配置下的行為

核心配置自旋鎖行為
單核、非搶佔編譯時完全優化移除(空操作)
單核、搶佔式僅作為 preempt_disable/enable 開關
多核完整功能:防止多核並發訪問 + 防止搶佔競爭

七、Linux 核心搶佔發生時機

用戶搶佔

  • 系統呼叫返回用戶空間
  • 中斷處理返回用戶空間

核心搶佔

  • 中斷處理返回核心空間,且核心可搶佔
  • 核心程式碼重新獲得可搶佔性(如 spin_unlock 後)
  • 核心任務顯式呼叫 schedule()
  • 核心任務阻塞(睡眠)

八、實時性優化:中斷喚醒等待進程

I/O 事件發生 → 中斷處理程式喚醒等待進程 → 設定 need_resched → 中斷返回觸發調度 → 等待進程獲得 CPU → 毫秒級響應,與時間片長度無關。

Copyright © Windows教程網 All Rights Reserved