typedef struct {
volatile unsigned long lock; // 鎖標誌:0=空閒, 1=持有
volatile unsigned long tid; // 持有者線程 ID (syscall(SYS_gettid))
volatile unsigned long recur; // 遞歸計數(支持同一線程重入)
} spin_lock_t;
#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 等。
// 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 |
| 信號安全 | 不安全 | 信號處理函數中調用會死鎖(持有鎖時被中斷再加鎖) |
spinlock_t/rwlock_t:內核模塊開發必用。atomic_flag:標準化無鎖原語,atomic_flag_test_and_set_explicit。自旋鎖核心在於原子指令 + 內存屏障 + 忙等待退讓。現代開發優先使用標準庫/系統庫實現,僅在極端性能敏感、無法使用庫的場景手寫。切記:信號處理函數中禁用自旋鎖,否則必死鎖。