首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux 自旋鎖原理與無鎖實現:從 GCC 內建到內聯匯編的演進

     編輯:關於C語言

自旋鎖適用場景

  • 鎖持有時間極短(微秒級),上下文切換開銷大於忙等待。
  • 中斷上下文、無法睡眠的路徑。
  • 多核 SMP 系統。

核心數據結構


typedef struct {
    volatile unsigned long lock;   // 鎖標誌:0=空閒, 1=持有
    volatile unsigned long tid;    // 持有者線程 ID (syscall(SYS_gettid))
    volatile unsigned long recur;  // 遞歸計數(支持同一線程重入)
} spin_lock_t;

原子操作抽象層:跨編譯器/架構適配

GCC ≥ 4.1 內建函數(推薦)


#define atomic_test_and_set(ptr) __sync_lock_test_and_set(ptr, 1)
#define atomic_clear(ptr)        __sync_lock_release(ptr)
  • 自帶全內存屏障,映射為 lock; xchg / lock; cmpxchg 等。
  • 可移植、可讀、無需內聯匯編。

x86/x86_64 內聯匯編回退(舊編譯器)


// Test-and-Set (CMPXCHG)
static inline unsigned long atomic_test_and_set(volatile unsigned long *a) {
    const unsigned long n = 1, old = 0;
    unsigned long ret;
    __asm__ __volatile__(
        "lock; cmpxchgl %3, %1;"
        : "=a"(ret)
        : "m"(*a), "a"(old), "r"(n)
        : "memory", "cc"
    );
    return ret; // 返回舊值:0=成功獲取, 1=已被持有
}

// Clear (XCHG)
static inline void atomic_clear(volatile unsigned long *a) {
    unsigned long ret, n = 0;
    __asm__ __volatile__(
        "lock; xchgl %0, %1"
        : "=r"(ret)
        : "m"(*a), "0"(n)
        : "memory"
    );
}
  • lock 前綴確保總線鎖定/緩存鎖定,多核原子性。
  • cmpxchgl:比較 EAX 與目標,相等則存入新值,否則讀取目標到 EAX。
  • xchgl:原子交換寄存器與內存值。

鎖操作實現

初始化


static inline void spin_lock_init(spin_lock_t *lk) {
    lk->lock = lk->tid = lk->recur = 0;
}

加鎖(帶遞歸與退讓)


#define SPIN_YIELD_INTERVAL 63U // 每 64 次嘗試 sched_yield() 一次

static inline int spin_lock_lock(spin_lock_t *lk) {
    pid_t me = syscall(SYS_gettid);
    int spins = 0;
    for (;;) {
        if (!atomic_test_and_set(&lk->lock)) { // 成功獲取
            lk->tid = me;
            lk->recur = 1;
            return 0;
        }
        if (lk->tid == me) // 遞歸重入
            return ++lk->recur;

        if (!(++spins & SPIN_YIELD_INTERVAL))
            sched_yield(); // 退讓 CPU,減少總線爭用
    }
}

解鎖


static inline int spin_lock_unlock(spin_lock_t *lk) {
    unsigned long me = syscall(SYS_gettid);
    if (lk->tid != me) return -1; // 非持有者嘗試解鎖
    if (--lk->recur == 0) {       // 最後一層遞歸
        lk->tid = 0;
        atomic_clear(&lk->lock);
    }
    return 0;
}

關鍵設計權衡

特性實現選擇說明
遞歸支持tid + recur 計數同線程多次加鎖不死鎖,但增加開銷
公平性無(簡單 TAS)可能導致飢餓,生產級用票鎖/隊列鎖
退讓策略指數退避近似 (2^n)SPIN_YIELD_INTERVAL=63 即每 64 次 yield
信號安全不安全信號處理函數中調用會死鎖(持有鎖時被中斷再加鎖)

生產級替代方案

  • pthread_spinlock_t:POSIX 標準,內核優化,支持進程共享屬性。
  • Linux 內核 spinlock_t/rwlock_t:內核模塊開發必用。
  • C11 atomic_flag:標準化無鎖原語,atomic_flag_test_and_set_explicit。
  • 用戶態 RCU (urcu):讀多寫少場景極致性能。

總結

自旋鎖核心在於原子指令 + 內存屏障 + 忙等待退讓。現代開發優先使用標準庫/系統庫實現,僅在極端性能敏感、無法使用庫的場景手寫。切記:信號處理函數中禁用自旋鎖,否則必死鎖。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved