尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
ARM 架构下 spin_lock 实现
阅读该文章前需要对原子指令有所了解推荐阅读 聊一聊原子操作和弱内存序1、概念内核当发生访问资源冲突的时候可以有两种锁的解决方案选择一个是原地等待一个是挂起当前进程调度其他进程执行睡眠Spinlock 是内核中提供的一种比较常见的锁机制自旋锁是“原地等待”的方式解决资源冲突的即一个线程获取了一个自旋锁后另外一个线程期望获取该自旋锁获取不到只能够原地“打转”忙等待。由于自旋锁的这个忙等待的特性注定了它使用场景上的限制 —— 自旋锁不应该被长时间的持有消耗 CPU 资源。2、源码实现2.1 内联汇编asm(code : output operand list : input operand list : clobber list);这种嵌入汇编的形式一共分为四个部分code汇编的操作代码一条或者多条指令如果是多条指令需要在指令间使用 \n\t 隔开。与通用的汇编代码有一些不同因为支持 C 变量的操作所以在操作由第二、三部分提供的操作数时使用 %n 来替代操作数output operand list表示输出的操作数通常是一个或者多个 C 函数中的变量input operand list表示输入的操作数通常是一个或者多个 C 函数中的变量clobber list告诉编译器这段汇编代码会修改哪些寄存器或状态2.2 ARM32 上 spinlock 实现arch\arm\include\asm\spinlock.h#defineTICKET_SHIFT16typedefstruct{union{u32 slock;struct__raw_tickets{#ifdef__ARMEB__u16 next;u16 owner;#elseu16 owner;u16 next;#endif}tickets;};}arch_spinlock_t;/* * ARMv6 ticket-based spin-locking. * * A memory barrier is required after we get a lock, and before we * release it, because V6 CPUs are assumed to have weakly ordered * memory. */staticinlinevoidarch_spin_lock(arch_spinlock_t*lock){unsignedlongtmp;u32 newval;arch_spinlock_tlockval;prefetchw(lock-slock);__asm____volatile__(1: ldrex %0, [%3]\n add %1, %0, %4\n strex %2, %1, [%3]\n teq %2, #0\n bne 1b:r(lockval),r(newval),r(tmp):r(lock-slock),I(1TICKET_SHIFT):cc);while(lockval.tickets.next!lockval.tickets.owner){wfe();lockval.tickets.ownerREAD_ONCE(lock-tickets.owner);}smp_mb();}staticinlinevoidarch_spin_unlock(arch_spinlock_t*lock){smp_mb();lock-tickets.owner;dsb_sev();}在内联汇编中%0、%1、%2… 是自动编号的操作数它们在 “输出” 和 “输入” 约束中按顺序出现。例如%0 代指 lockval %1 代指 newval %4 代指 (1 TICKET_SHIFT)…汇编C语言解释1: ldrex %0, [%3]lockval lock读取锁的值赋值给 lockvaladd %1, %0, %4newval lockval (1 16)将 next 之后的值存在 newval 中strex %2, %1, [%3]lock newval更新 lock 中的值将是否成功结果存入在 tmp 中teq %2, #0if(tmp 0)判断上条指令是否成功如果不成功执行 ”bne 1b” 跳到标号1执行注arch_spinlock_t是一个联合体其内部仅包含一个slock成员。因此对lock-slock的访问实质上就是对整个arch_spinlock_t结构的访问在 ARM32 架构下该自旋锁的实现本质上是一个基于队列的排队锁ticket lock。每次调用arch_spin_lock()获取锁时都会对锁的next字段执行原子加一操作。next的值相当于调用者在等待队列中排到的“号码”解锁操作通常由 arch_spin_unlock() 实现会对 owner 字段执行原子加一使得下一个排队等待的线程可以获取锁上面所说的 “原子加一” 实际上是通过原子指令 ldrex、strex 实现的。原子指令确保了排队的正确性不会出现两个任务获取到相同号码的情况这是一种基于 “排队取号” 模型的公平自旋锁由 next发号计数器和 owner服务计数器两个变量协同工作拿号排队每个试图加锁的线程都会原子性地从 next领取一个专属号码并推动 next 递增。这确立了线程获取锁的先后次序轮询等待线程通过循环不断比对 自身号码 与 当前服务号码owner。只有当两者一致时才意味着轮到该线程执行从而获得锁叫号放行锁释放时线程只需将 owner 原子加 1。这一操作隐式地通知了队列中的下一个线程“现在轮到你了”从而实现锁的平滑移交该机制通过将竞争转化为有序的队列等待有效避免了线程饥饿问题。3、WFE/SEV 的事件通知机制3.1 WFE 为什么出现在自旋循环中如果等待者一直执行普通的紧凑轮询它会高频读取共享缓存行增加功耗和内存系统压力。WFEWait For Event向处理器表明当前执行流正在等待事件具体处理器实现可以在等待期间暂停取指甚至进入低功耗状态。当事件到达时WFE返回代码重新读取owner。唤醒并不等于获得锁事件可能与当前锁无关也可能同时唤醒多个处理器所以判断条件必须放在循环中反复检查。这和条件变量常见的“被唤醒后仍需检查条件”是同一个原则。3.2 SEV 如何通知等待者SEVSend Event向系统中的处理器发送事件。当前持锁者执行owner后再执行SEV等待者从WFE返回并重新检查owner。只有票号等于新owner的等待者可以进入临界区其余等待者继续执行WFE。ARM 为每个处理器维护事件寄存器可以把它理解成一个一位的事件标志标志已经置位时执行WFE指令会清除标志并直接返回标志未置位时执行WFE处理器可以等待后续事件SEV会使对应的事件标志置位并使正在等待事件的处理器恢复执行。这个机制可以避免典型的“先通知、后等待”丢失唤醒问题。即使释放者恰好在等待者即将执行WFE之前发出了SEV事件也会先记录在事件寄存器中等待者随后执行WFE时会消费该事件并立即返回然后重新读取owner。3.3 WFE 和 WFI 的区别指令全称主要等待对象在此处的用途WFEWait For Event体系结构定义的事件SEV是其中的重要来源等待锁状态变化释放方可主动发送事件WFIWait For Interrupt体系结构定义的 WFI 唤醒事件WFI wake-up events常用于 CPU 空闲路径不适合直接构成这里的锁交接协议按照 Arm 的术语WFI会等待体系结构定义的WFI wake-up event不宜笼统表述为“手动唤醒”或“异常唤醒”。SEV不是WFI的配对通知指令。具体唤醒条件会随体系结构版本、处理器状态和实现而有所不同请查阅对应版本的 Arm Architecture Reference Manual。本文只需关注自旋锁等待的是另一个 CPU 对共享锁状态的更新解锁方会执行SEV因此等待方使用与之配合的WFE而不是WFI。
RELATED

相关推荐

CentOS源码编译安装Python 3.10完整指南与常见坑

CentOS源码编译安装Python 3.10完整指南与常见坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/13 13:04:48
电容位置如何导致EMC辐射不降反增

电容位置如何导致EMC辐射不降反增

1. 这不是电容没加够,是它“站错队”了——EMC整改中最隐蔽的失效陷阱 刚接手一个工业网关项目的EMC整改,客户在30MHz~200MHz频段辐射超标12dB,远超Class B限值。测试工程师指着频谱图上那几簇尖锐的峰说:“电源入口加了两级π型滤…

📅 2026/9/13 13:04:48
React Scan 接入 Remix 全指南:Script Tag、模块导入与生产环境检测

React Scan 接入 Remix 全指南:Script Tag、模块导入与生产环境检测

React Scan 接入 Remix 全指南:Script Tag、模块导入与生产环境检测 【免费下载链接】react-scan Scan and fix React performance issues 项目地址: https://gitcode.com/GitHub_Trending/re/react-scan 导读 本文基于 React Scan 官方安装文档&#xff0c…

📅 2026/9/13 13:04:48
MORE NEWS

更多资讯

📰

UEFITool-UE固件解析工具深度指南:CSE/RTS/GPT修复与Secure Boot审计

简介:本资源是面向UEFI固件开发者、系统安全工程师及固件逆向研究者的专业工具包,提供UEFITool-UE 0.10.0稳定版本源码,用于深度解析、调试与定制UEFI固件镜像。包内共70个文件,以33个头文件(h)、18个C源码…

📰

陷波与反陷波滤波器原理及应用对比

1. 陷波滤波器与反陷波滤波器的本质区别陷波滤波器(Notch Filter)和反陷波滤波器(Anti-Notch Filter)是信号处理领域一对互补的滤波器类型,它们就像光谱中的阴阳两面。陷波滤波器的主要功能是抑制特定频段的信号&#…

📰

爬虫工程师进阶指南:从基础到分布式架构实战

1. 爬虫工程师的自我修养:从入门到进阶的完整路径 十年前我刚接触爬虫时,以为用几行requests.get()就能走遍天下,直到连续被封了三个IP才意识到这个领域的深度。现在看新手们重复着我当年的错误,决定把爬虫工程师的真实成长路线整…

📰

Agent-Skills-for-Context-Engineering 基准评测体系全解:从确定性防作弊到技能有效性验证

Agent-Skills-for-Context-Engineering 基准评测体系全解:从确定性防作弊到技能有效性验证 【免费下载链接】Agent-Skills-for-Context-Engineering A comprehensive collection of Agent Skills for context engineering, multi-agent architectures, and producti…

📰

Vue开发工具链:Volar+Prettier+ESLint配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

电商运营三大隐性成本解析与管控策略

1. 项目概述:电商运营中的隐性成本洞察在电商行业摸爬滚打多年后,我发现一个有趣的现象:许多头部商家虽然GMV惊人,但实际利润率往往低于行业平均水平。去年执教SKA172期电商高阶运营课程时,通过对37家年销过亿店铺的财…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬