关抢占 自旋锁_Linux学习第28节,什么是自旋锁?内核是如何设计,如何实现它的...

上一节主要介绍了 Linux内核中的原子操作,在某种程度上避免了多个线程对同一全局变量的竞争问题。要是内核中的其他C语言程序开发中的临界区都能像上一节介绍的原子变量那样简单就好了。

15f41a692c403251d8dc675314b77ac8.png

然而事与愿违,在某个C语言项目中,可能某个临界区甚至会跨越多个函数。例如,函数 A 负责从共享数据结构中取出数据,函数 B 负责处理这些数据,函数 C 则负责将这些数据分发。显然,在这个过程中,要保护共享数据结构,仅仅依赖上一节介绍的原子操作是远远不够的,这就需要更为复杂的同步方法——锁。

自旋锁简介

Linux 内核开发中,最常使用的锁是自旋锁。如果线程 A 获得了自旋锁,其他线程再请求锁的时候就无法获得,必须等待线程 A 释放自旋锁。也就是说,一个自旋锁同时只能被一个线程持有,用其保护共享资源就太合适了。

若线程 B 请求一个已经被线程 A 持有的自旋锁,则线程 B 会一直执行类似 while(1); 的自旋动作,直到线程 A 释放自旋锁。能够看出,自旋等待非常耗费处理器,因此任何线程都不应该长时间持有自旋锁。

Linux 内核还有其他类型锁的设计,它会让线程 B 睡眠,直到线程 A 释放自旋锁的时候才重新唤醒线程 B。这样处理器就不必白白把时间花在等待上,而是可以在此期间执行其他代码。那为什么还要设计自旋锁呢?

89b944fb0c170a63b84ea8330bb80c53.png

其实,让线程 B 睡眠再唤醒也是有一定的开销的:至少有两次上下文切换。如果这两次上下文切换的开销超出了自旋锁让处理器等待的开销,那明显使用自旋锁更好些。可以看出,自旋锁是一种非常轻量级的设计,在抢占式的 Linux 内核开发中占有非常重要的席位。

自旋锁的C语言代码实现

使用自旋锁是简单的,基本操作就是初始化锁、请求锁、释放锁,用C语言代码描述这一过程就是:

DEFINE_SPINLOCK(lock);spin_lock(&lock);/* 临界区 */spin_unlock(&lock);

从上述C语言代码可以看出,在使用自旋锁之前,应先初始化,DEFINE_SPINLOCK() 是一个宏,它的C语言代码如下:

#define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
ab348255c89a10ccdfbafc17df8e2090.png

显然,DEFINE_SPINLOCK() 宏其实就是定义了一个 spinlock_t 类型的变量,并对其赋了初值,spinlock_t 是一个结构体,它的C语言代码如下,请看:

20 typedef struct {| 21 raw_spinlock_t raw_lock;| 22 #ifdef CONFIG_GENERIC_LOCKBREAK| 23 unsigned int break_lock;| 24 #endif| 25 #ifdef CONFIG_DEBUG_SPINLOCK| 26 unsigned int magic, owner_cpu;| 27 void *owner;| 28 #endif| 29 #ifdef CONFIG_DEBUG_LOCK_ALLOC| 30 struct lockdep_map dep_map;| 31 #endif| 32 } spinlock_t;
b38ff2510ef262b23f637ceac8e087fc.png

自旋锁初始化后,在进入需要保护的临界区之前,应先调用 spin_lock() 请求自旋锁。spin_lock() 也是一个宏,它的C语言代码如下,请看:

#define spin_lock(lock) _spin_lock(lock)

继续跟踪 _spin_lock(),会发现其实自旋锁的最终核心功能由 __raw_spin_lock() 函数实现, __raw_spin_lock()函数的实现是因平台而异的,在x86 平台下,它的C语言代码如下,请看:

135 static __always_inline void __raw_spin_lock(raw_spinlock_t *lock)- 136 {| 137 int inc = 0x00010000;| 138 int tmp;| 139 | 140 asm volatile("lock ; xaddl %0, %1"| 141 "movzwl %w0, %2"| 142 "shrl $16, %0"| 143 "1:"| 144 "cmpl %0, %2"| 145 "je 2f"| 146 "rep ; nop"| 147 "movzwl %1, %2"| 148 /* don't need lfence here, because loads are in-order */| 149 "jmp 1b"| 150 "2:"| 151 : "+Q" (inc), "+m" (lock->slock), "=r" (tmp)| 152 :| 153 : "memory
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值