[Linux Kernel] spinlock 自旋锁 官方文档

Lesson 1: Spin locks

最基本的锁定原语是spinlock:

#include <linux/spinlock.h>

  static DEFINE_SPINLOCK(xxx_lock);

	unsigned long flags;

	spin_lock_irqsave(&xxx_lock, flags);
	... critical section here ..
	spin_unlock_irqrestore(&xxx_lock, flags);

   上面的总是安全的。它将禁用本地中断,但自旋锁本身将保证全局锁定,因此它将保证受该锁定保护的区域内只有一个控制线程。这甚至在UP下也能很好地工作,因此代码不需要担心 UP 与 SMP 的问题1:spinlocks在这两种情况下都能正常工作。

注意! 自旋锁对内存的影响在中有进一步的描述:

       Documentation/memory-barriers.txt
           (5) LOCK operations.
           (6) UNLOCK operations.

   上面的方法通常非常简单(大多数情况下,您通常只需要一个spinlock——使用多个spinlock可以使事情变得更复杂,甚至更慢,而且通常只对需要拆分的序列有用:如果您不确定,请不惜一切代价避免它)。

   这是关于自旋锁的唯一真正困难的部分:一旦开始使用自旋锁,它们往往会扩展到您以前可能没有注意到的区域,因为您必须确保自旋锁在使用它们的任何地方都能正确地保护共享数据结构。自旋锁最容易添加到完全独立于其他代码的位置(例如,没有其他人接触过的内部驱动程序数据结构体中)。

注意! The spin-lock is safe only when you also use the lock itself to do locking across CPU’s, which implies that EVERYTHING that touches a shared variable has to agree about the spinlock they wan to use.

Lesson 2: reader-writer spinlocks

   如果您的数据访问有一个非常自然的模式,您通常倾向于从共享变量中读取数据,那么读写器锁(rw_lock)版本的自旋锁有时是有用的。它们允许多个 reader 同时处于同一个关键区域,但如果有人想要更改变量,则必须获得独占的写锁。

注意! 读写锁比简单的自旋锁需要更多的 atomic memory operations 。除非 reader 关键部分很长,否则最好使用自旋锁。

例程看起来如下:

   rwlock_t xxx_lock = __RW_LOCK_UNLOCKED(xxx_lock);

	unsigned long flags;

	read_lock_irqsave(&xxx_lock, flags);
	.. critical section that only reads the info ...
	read_unlock_irqrestore(&xxx_lock, flags);

	write_lock_irqsave(&xxx_lock, flags);
	.. read and write exclusive access to the info ...
	write_unlock_irqrestore(&xxx_lock, flags);

   上述类型的锁对于复杂的数据结构(如链表)可能很有用,特别是在不更改列表本身的情况下搜索条目。读锁允许多个并发 reader。任何更改列表的操作都必须获得写锁。

注意! RCU更适合列表遍历,但需要仔细注意设计细节。(见文档 Documentation/RCU/listRCU.rst)

   此外,您不能将读锁“升级”为写锁,因此如果您在任何时候需要进行任何更改(即使您不是每次都这样做),您必须在一开始就获得写锁。

注意! 在大多数情况下,我们都在努力删除读写器自旋锁,因此请不要在没有一致意见的情况下添加新的自旋锁。(Instead, see Documentation/RCU/rcu.rst for complete information.)

Lesson 3: spinlocks revisited

   上面的单自旋锁原语决不是唯一的。它们是最安全的,在任何情况下都能工作,但部分原因是它们是安全的,它们也相当缓慢。它们比需要的要慢,因为它们必须禁用中断(这只是x86上的一条指令,但它很昂贵——在其他架构上可能更糟)。

   如果您需要保护跨多个CPU的数据结构,并且希望使用自旋锁,则可以使用更便宜的自旋锁版本。如果您知道自旋锁从不用于中断处理程序,则可以使用 non-irq 版本:

	spin_lock(&lock);
	...
	spin_unlock(&lock);

   (当然,还有同等的读写锁的版本)。spinlock 将保证相同类型的独占访问,而且速度会快得多。如果您知道所讨论的数据只在“流程上下文”中进行过操作(即不涉及中断),这将非常有用。

如果有使用自旋锁的中断,则不能使用这些版本的原因是,可能会出现死锁:

	spin_lock(&lock);
	...
		<- interrupt comes in:
			spin_lock(&lock);

where an interrupt tries to lock an already locked variable. This is ok if the other interrupt happens on another CPU, but it is not ok if the interrupt happens on the same CPU that already holds the lock, because the lock will obviously never be released (because the interrupt is waiting for the lock, and the lock-holder is interrupted by the interrupt and will not continue until the interrupt has been processed).

(This is also the reason why the irq-versions of the spinlocks only need to disable the local interrupts - it’s ok to use spinlocks in interrupts on other CPU’s, because an interrupt on another CPU doesn’t interrupt the CPU that holds the lock, so the lock-holder can continue and eventually releases the lock).

                Linus

Reference information

对于动态初始化,请根据需要使用 spin_lock_init() 或 rwlock_init() :

   spinlock_t xxx_lock;
   rwlock_t xxx_rw_lock;

   static int __init xxx_init(void)
   {
	spin_lock_init(&xxx_lock);
	rwlock_init(&xxx_rw_lock);
	...
   }

   module_init(xxx_init);

对于静态初始化,请根据需要使用:

  • DEFINE_SPINLOCK() / DEFINE_RWLOCK()
  • __SPIN_LOCK_UNLOCKED() / __RW_LOCK_UNLOCKED()

参考

  • linux-5.4.37/Documentation/locking/spinlocks.rst

  1. UP(Uni-Processor):系统只有一个处理器单元,即单核CPU系统。

    SMP(Symmetric Multi-Processors):系统有多个处理器单元。各个处理器之间共享总线,内存等等。在操作系统看来,各个处理器之间没有区别。

    要注意,这里提到的“处理器单元”是指“logic CPU”,而不是“physical CPU”。举个例子,如果一个“physical CPU”包含2个core,并且一个core包含2个hardware thread。则一个“处理器单元”就是一个hardware thread。 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值