专注Java教育14年 全国咨询/投诉热线:400-8080-105
动力节点LOGO图
始于2009,口口相传的Java黄埔军校
首页 hot资讯 什么是自旋锁

什么是自旋锁

更新时间:2022-09-14 09:45:40 来源:动力节点 浏览1032次

Linux 内核中最常见的锁是自旋锁。自旋锁是一种最多可以由一个执行线程持有的锁。如果执行线程在竞争(已持有)时尝试获取自旋锁,则线程繁忙循环自旋等待锁变得可用。如果锁没有被争用,线程可以立即获取锁并继续。旋转可防止多个执行线程在任何时候进入临界区。请注意,可以在多个位置使用同一个锁,因此可以保护和同步对给定数据结构的所有访问。

回到上一章的门和钥匙类比,自旋锁类似于坐在门外,等待里面的人出来递给你钥匙。如果你走到门口,里面没有人,你可以拿钥匙进入房间。如果您到达门口并且有人正在里面,您必须在外面等待钥匙,有效地反复检查它的存在。房间空了,你可以拿钥匙进去。多亏了钥匙(读:自旋锁),房间内(读:临界区)同时只允许一个人(读:执行线程)进入。

竞争自旋锁导致线程在等待锁变为可用时自旋(实质上是浪费处理器时间)这一事实很重要。这种行为是自旋锁的关键。它不是明智的做法是长时间持有自旋锁。这就是自旋锁的本质:一个轻量级的单持有人锁,应该在短时间内持有。争用锁时的另一种行为是将当前线程置于睡眠状态,并在它可用时将其唤醒。然后处理器可以关闭并执行其他代码。这会产生一些开销,最明显的是切换出和切换回阻塞线程所需的两个上下文切换,这肯定比用于实现自旋锁的几行代码要多得多。因此,保持自旋锁的时间少于两次上下文切换的持续时间是明智的。因为我们大多数人都有比测量上下文切换更好的事情要做,所以尽量缩短锁定时间。

自旋锁依赖于体系结构并在汇编中实现。与架构相关的代码在<asm/spinlock.h>中定义。实际可用的接口在<linux/spinlock.h>中定义。自旋锁的基本用途是

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
自旋锁(&mr_lock);
/* 临界区 */
自旋解锁(&mr_lock);

锁最多只能由一个执行线程同时持有。因此,一次只允许一个线程进入临界区。这为多处理机器上的并发提供了所需的保护。在单处理器机器上,锁编译掉并且不存在。它们只是充当禁用和启用内核抢占的标记。如果内核抢占关闭,锁会完全编译掉。

自旋锁可用于中断处理程序,而信号量不能使用,因为它们处于休眠状态。如果在中断处理程序中使用了锁,您还必须在获得锁之前禁用本地中断(当前处理器上的中断请求)。否则,中断处理程序可能会在持有锁时中断内核代码并尝试重新获取锁。中断处理程序旋转,等待锁变得可用。然而,锁持有者在中断处理程序完成之前不会运行。这是上一章讨论的双重获取死锁的一个例子。请注意,您只需要在当前处理器。如果一个中断发生在不同的处理器上,并且它在同一个锁上旋转,它不会阻止锁持有者(它在不同的处理器上)最终释放锁。

内核提供了一个方便地禁用中断和获取锁的接口。用法是

spinlock_t mr_lock = SPIN_LOCK_UNLOCKED;
无符号长标志;
spin_lock_irqsave(&mr_lock, flags);
/* 临界区 ... */
spin_unlock_irqrestore(&mr_lock, flags);

例程spin_lock_irqsave()保存中断的当前状态,在本地禁用它们,然后获得给定的锁。相反,spin_unlock_irqrestore()解锁给定的锁并将中断返回到它们之前的状态。这样,如果中断最初被禁用,您的代码不会错误地启用它们,而是保持禁用它们。请注意,flags变量似乎是按值传递的。这是因为锁定例程部分实现为宏。

在单处理器系统上,前面的示例仍然必须禁用中断以防止中断处理程序访问共享数据,但锁定机制已被编译掉。锁定和解锁也分别禁用和启用内核抢占。如果大家想了解更多相关知识,不妨来关注一下动力节点的Linux教程,里面还有更丰富的知识等着大家去学习,相信对大家一定会有所帮助的。

提交申请后,顾问老师会电话与您沟通安排学习

免费课程推荐 >>
技术文档推荐 >>