Linux 自旋锁

1.什么是自旋锁
自旋锁顾名思义首先是一把锁,另外使用这把锁的线程需要反复自我循环(loop)检测这把锁是否可用。
注意与信号量区别,信号量也是一把锁,但是使用这把锁的线程检测锁不可用时,选择去睡眠,而不是自我循环。
自旋锁与信号量相同点是两者都是锁,都具备锁定特性,实现临界区代码块的同步与互斥访问。

2.自旋锁实现(摘自http://en.wikipedia.org/wiki/Spinlock)
不同体系结构不同,所以实现自旋锁方式不一样,下面是Intel 80386简单实现,可以进行进一步优化,详细代码参照Linux内核。 功能反复测试
; Intel syntax
 
lock:                        ; The lock variable. 1 = locked, 0 = unlocked.
     dd      0
 
spin_lock:
     mov     eax, 1          ; Set the EAX register to 1.
 
     xchg    eax, [lock]     ; Atomically swap the EAX register with
                             ;  the lock variable.
                             ; This will always store 1 to the lock, leaving
                             ;  previous value in the EAX register.
 
     test    eax, eax        ; Test EAX with itself. Among other things, this will
                             ;  set the processor's Zero Flag if EAX is 0.
                             ; If EAX is 0, then the lock was unlocked and
                             ;  we just locked it.
                             ; Otherwise, EAX is 1 and we didn't acquire the lock.
 
     jnz     spin_lock       ; Jump back to the MOV instruction if the Zero Flag is
                             ;  not set; the lock was previously locked, and so
                             ; we need to spin until it becomes unlocked.
 
     ret                     ; The lock has been acquired, return to the calling
                             ;  function.
 
spin_unlock:
     mov     eax, 0          ; Set the EAX register to 0.
 
     xchg    eax, [lock]     ; Atomically swap the EAX register with
                             ;  the lock variable.
 
     ret                     ; The lock has been released

3.自旋锁使用注意事项
由于自旋锁最主要特性是不断反复循环测试,注定下列场景需注意:
1.临界区代码不要存在睡眠情况(主要因为发生睡眠不可预知睡眠多长时间,另外长时间睡眠,导致即将进入临界区其他线程,长时间得不到自旋锁,无休止自旋,从而导致死锁),所以临界区调用导致睡眠函数,不能选择自旋锁。
2.保证进入临界区的线程,不发生内核抢占。(这一点不必担心,持有自旋锁情况,Linux内核不进行抢占)
3.临界区代码,执行时间不能太长。(因为其他线程,如果要进入话,导致自旋,过多消耗CPU资源)
4.选择自旋锁时,也要注意中断情况(上半部分中断(硬件中断)和下半部分中断(软中断),中断会抢占即中断到来时,打断目前临界区代码执行,转往执行中断代码),当中断要进入自旋锁保护临界区代码时,将导致线程与中断发生死锁可能。

4Linux内核中提供自旋锁函数
1)自旋锁头文件
<linux/spinlock.h>

2)自旋锁数据类型
spinlock_t

3)自旋锁变量静态初始化
spinlock_t lock = SPIN_LOCK_UNLOCKED;

4)自旋锁变量动态初始化
spinlock_t lock
spin_lock_init(&lock);

5)锁定函数
void spin_lock(spinlock_t *lock);
void spin_lock_irqsave(spinlock_t *lock, unsigned long flags);
void spin_lock_irq(spinlock_t *lock); 禁止(屏蔽硬件中断)自旋锁,针对场合4情况
void spin_lock_bh(spinlock_t *lock);禁止(屏蔽下半部分中断)自旋锁,针对场合4情况

6)解锁函数
void spin_unlock(spinlock_t *lock);
void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags);
void spin_unlock_irq(spinlock_t *lock);
void spin_unlock_bh(spinlock_t *lock);

5.自旋锁例子
不提供,因为上述头文件,初始化,使用非常详细。
### Linux 自旋锁 API 函数 #### 初始化自旋锁 为了创建并初始化一个新的自旋锁,可以使用 `spin_lock_init` 函数: ```c void spin_lock_init(spinlock_t *lock); ``` 此函数接受指向 `spinlock_t` 类型变量的指针作为参数,并将其初始化为未锁定状态。 #### 获取自旋锁 获取自旋锁可以通过调用 `spin_lock` 来完成: ```c void spin_lock(spinlock_t *lock); ``` 该操作会使当前执行路径等待直到获得指定的锁。如果锁已经被其他处理器持有,则当前CPU会在忙等循环中持续尝试获取锁[^5]。 对于不可抢占内核配置下的单处理机(UP),宏定义简化了这一过程[^3]。 #### 释放自旋锁 当不再需要保持独占访问权时,应该通过下面的方法来解锁: ```c void spin_lock_unlock(spinlock_t *lock); ``` 这允许其他可能正在等待相同资源的任务继续前进。 #### 尝试获取自旋锁而不阻塞 有时希望测试能否立即取得锁而不想陷入长时间等待的情况,这时可选用 `spin_trylock` 方法: ```c int spin_trylock(spinlock_t *lock); ``` 成功返回非零值;失败则返回0表示未能立刻占有目标对象。 需要注意的是,在多处理器环境中或启用了预占特性的单一中央单元上应用这些接口特别重要,因为它们能有效防止竞争条件的同时确保高效性能表现。 此外,被自旋锁保护的关键区域绝对不允许调用可能导致休眠的操作系统服务,以免引发潜在的死锁风险。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值