|
自某日看了iceboy和MJ0011关于多处理器同步的一些讨论,才发现原来我对自旋锁的理解也有一定错误,还好现在明白了~~为了加深理解,就深入分析了一下自旋锁的实现,一篇小小的自旋锁分析文章,献给大家。写得比较碎,欢迎各位大牛小牛指出错误~ 一、自旋锁是什么? 先进行下简单科普,自旋锁是一种轻量级的多处理器间的同步机制。因此,自旋锁对于单处理器是没有实际意义的。它要求持有锁的处理器所占用的时间尽可能短,因为此时别的处理器正在高速运转并等待锁的释放,所以不能长时间占有。 曾经有个经典的例子来比喻自旋锁:A,B两个人合租一套房子,共用一个厕所,那么这个厕所就是共享资源,且在任一时刻最多只能有一个人在使用。当厕所闲置时,谁来了都可以使用,当A使用时,就会关上厕所门,而B也要使用,但是急啊,就得在门外焦急地等待,急得团团转,是为“自旋”,呵呵。这个比喻还算恰当吧,大家也明白为什么要求锁的持有时间尽量短了吧!尤其b4占着茅坑不拉屎的行为~~ 二、操作系统如何实现自旋锁? 在Linux和Windows中都实现了自旋锁,下面我们就来看一看Windows下是如何实现的吧。 自旋锁的结构: KSPIN_LOCK SpinLock; KSPIN_LOCK实际是一个操作系统相关的无符号整数,32位系统上是32位的unsigned long,64位系统则定义为unsigned __int64。 在初始化时,其值被设置为0,为空闲状态。 参见WRK: FORCEINLINE VOID NTAPI KeInitializeSpinLock ( __out PKSPIN_LOCK SpinLock ) { *SpinLock = 0; } 关于自旋锁的两个基本操作:获取和释放 VOID KeAcquireSpinLock( IN PKSPIN_LOCK SpinLock, OUT PKIRQL OldIrql ); VOID KeReleaseSpinLock( IN PKSPIN_LOCK SpinLock, IN KIRQL NewIrql ); 获取时做了哪些工作呢? Ntddk.h中是这样定义的: #define KeAcquireSpinLock(SpinLock, OldIrql) / *(OldIrql) = KeAcquireSpinLockRaiseToDpc(SpinLock) 很明显,核心的操作对象是SpinLock,同时也与IRQL有关。 再翻翻WRK,找到KeAcquireSpinLockRaiseToDpc的定义: __forceinline KIRQL KeAcquireSpinLockRaiseToDpc ( __inout PKSPIN_LOCK SpinLock ) { KIRQL OldIrql; // // Raise IRQL to DISPATCH_LEVEL and acquire the specified spin lock. // OldIrql = KfRaiseIrql(DISPATCH_LEVEL); KxAcquireSpinLock(SpinLock); return OldIrql; } 首先会提升IRQL到DISPATCH_LEVEL,然后调用KxAcquireSpinLock()。(若当前IRQL就是DISPATCH_LEVEL,那么就调用KeAcquireSpinLockAtDpcLevel,省去提升IRQL一步)。因为线程调度也是发生在DISPATCH_LEVEL,所以提升IRQL之后当前处理器上就不会发生线程切换。单处理器时,当前只能有一个线程被执行,而这个线程提升IRQL至DISPATCH_LEVEL之后又不会因为调度被切换出去,自然也可以实现我们想要的互斥“效果”,其实只操作IRQL即可,无需SpinLock。实际上单核系统的内核文件ntosknl.exe中导出的有关SpinLock的函数都只有一句话,就是return,呵呵。 而多处理器呢?提升IRQL只会影响到当前处理器,保证当前处理器的当前线程不被切换,那还得考虑其它处理器啊,继续看 KxAcquireSpinLock()函数吧。在WRK中找到的KxAcquireSpinLock()函数是Amd64位处理器上的代码(位于(/inc/private/ntos/inc/Amd64.h)中),32位x86的没找到。不过原理相通,一样可以参考 __forceinline VOID KxAcquireSpinLock ( __inout PKSPIN_LOCK SpinLock ) { if (InterlockedBitTestAndSet64((LONG64 *)SpinLock, 0))//64位函数 { KxWaitForSpinLockAndAcquire(SpinLock); //只有声明没有定义的函数,应该是做了测试,等待的工作 } } InterlockedBitTestAndSet64()函数的32位版本如下: ps:我汇编功底不太好,见谅~ BOOLEAN FORCEINLINE InterlockedBitTestAndSet ( IN LONG *Base, IN LONG Bit ) { __asm { mov eax, Bit mov ecx, Base lock bts [ecx], eax setc al }; } 关键就在bts指令,是一个进行位测试并置位的指令。,这里在进行关键的操作时有lock前缀,保证了多处理器安全。InterLockedXXX函数都有这个特点。显然,KxAcquireSpinLock()函数先测试锁的状态。若锁空闲,则*SpinLock为0,那么InterlockedBitTestAndSet()将返回0,并使*SpinLock置位,不再为0。这样KxAcquireSpinLock()就成功得到了锁,并设置锁为占用状态(*SpinLock不为0),函数返回。若锁已被占用呢?InterlockedBitTestAndSet()将返回1,此时将调用KxWaitForSpinLockAndAcquire()等待并获取这个锁。这表明,SPIN_LOCK为0则锁空闲,非0则已被占有。 由于WRK中仅有KxWaitForSpinLockAndAcquire()的声明而无定义,我们只能从名字猜测其做了什么。在WRK中看到了这两个函数: __forceinline BOOLEAN KxTryToAcquireSpinLock ( __inout PKSPIN_LOCK SpinLock ) { if (*(volatile LONG64 *)SpinLock == 0) { return !InterlockedBitTestAndSet64((LONG64 *)SpinLock, 0); } else { KeYieldProcessor(); return FALSE; } } 从名字看应该是试图获取自旋锁,先判断锁是否被占有。若空闲,则设置其为占用状态,这就成功地抢占了。若已被占用,则调用KeYieldProcessor(),这个函数其实只是一个宏: #define KeYieldProcessor() __asm { rep nop } //空转 都知道nop干啥的,CPU就是在空转进行等待而已。 下面这个函数则是仅测试自旋锁的状态: __forceinline BOOLEAN KeTestSpinLock ( __in PKSPIN_LOCK SpinLock ) { KeMemoryBarrierWithoutFence();//这个函数我也不知道干啥的 if (*SpinLock != 0) { KeYieldProcessor();//若被占用,则空转 return FALSE; } else { return TRUE; } } 好,看了获取部分,再看看释放锁的时候做了什么。 __forceinline VOID KeReleaseSpinLock ( __inout PKSPIN_LOCK SpinLock, __in KIRQL OldIrql ) { KxReleaseSpinLock(SpinLock);//先释放锁 KeLowerIrql(OldIrql);//恢复至原IRQL return; } 继续看KxReleaseSpinLock() __forceinline VOID KxReleaseSpinLock ( __inout PKSPIN_LOCK SpinLock ) { InterlockedAnd64((LONG64 *)SpinLock, 0);//释放时进行与操作设置其为0 } 好了,对于自旋锁的初始化、获取、释放,都有了了解。但是只是谈谈原理,看看WRK,似乎有种纸上谈兵的感觉?那就实战一下,看看真实系统中是如何实现的。以双核系统中XP SP2下内核中关于SpinLock的实现细节为例: 用IDA分析双核系统的内核文件ntkrnlpa.exe,关于自旋锁操作的两个基本函数是KiAcquireSpinLock和KiReleaseSpinLock,其它几个类似。 .text:004689C0 KiAcquireSpinLock proc near ; CODE XREF: sub_416FEE+2D p .text:004689C0 ; sub_4206C0+5 j ... .text:004689C0 lock bts dword ptr [ecx], 0 .text:004689C5 jb short loc_4689C8 .text:004689C7 retn .text:004689C8 ; --------------------------------------------------------------------------- .text:004689C8 .text:004689C8 loc_4689C8: ; CODE XREF: KiAcquireSpinLock+5 j .text:004689C8 ; KiAcquireSpinLock+12 j .text:004689C8 test dword ptr [ecx], 1 .text:004689CE jz short KiAcquireSpinLock .text:004689D0 pause .text:004689D2 jmp short loc_4689C8 .text:004689D2 KiAcquireSpinLock endp 代码比较简单,还原成源码是这样子的(偷懒用了F5): void __fastcall KiAcquireSpinLock(int _ECX) { while ( 1 ) { __asm { lock bts dword ptr [ecx], 0 } if ( !_CF ) break; while ( *(_DWORD *)_ECX & 1 ) __asm { pause }//应是rep nop,IDA将其翻译成pause } } fastcall方式调用,参数KSPIN_LOCK在ECX中,可以看到是一个死循环,先测试其是否置位,若否,则CF将置0,并将ECX置位,即获取锁的操作成功;若是,即锁已被占有,则一直对其进行测试并进入空转状态,这和前面分析的完全一致,只是代码似乎更精炼了一点,毕竟是实用的玩意嘛。 再来看看释放时: .text:004689E0 public KiReleaseSpinLock .text:004689E0 KiReleaseSpinLock proc near ; CODE XREF: sub_41702E+E p .text:004689E0 ; sub_4206D0+5 j ... .text:004689E0 mov byte ptr [ecx], 0 .text:004689E3 retn .text:004689E3 KiReleaseSpinLock endp 这个再清楚不过了,直接设置为0就代表了将其释放,此时那些如虎狼般疯狂空转的其它处理器将马上获知这一信息,于是,下一个获取、释放的过程开始了。这就是最基本的自旋锁,其它一些自旋锁形式是对这种基本形式的扩充。比如排队自旋锁,是为了解决多处理器竞争时的无序状态等等,不多说了。 现在对自旋锁可谓真的是明明白白了,之前我犯的错误就是以为用了自旋锁就能保证多核同步,其实不是的,用自旋锁来保证多核同步的前提是大家都要用这个锁。若当前处理器已占有自旋锁,只有别的处理器也来请求这个锁时,才会进入空转,不进行别的操作,这时你的操作将不会受到干扰。但是假如某个需要互斥的操作只有你这个线程才做而别人根本不去做(以iceboy的安全实现Inline Hook为例,请求锁、修改代码、释放锁的过程只有这个线程才会做,别的处理器上的 线程如果要执行这里还是照样执行,人家又不用修改),所以人家不请求锁时还是该干嘛干嘛啊,自己在那儿自旋只是一厢情愿。所以MJ说“别人都不跟你旋,你自己旋个头啊”,经典经典……. |
相关推荐
简单的非公平自旋锁以及基于排队的公平自旋锁的实现
基础 什么是自旋锁 由于本文主要讨论的都是自旋锁,所以首先就需要弄明白什么是自旋锁。 自旋锁最大的特征,就是它会一直循环检测锁的状态,当锁处于被占用的状态时,不会将当前线程阻塞住,而是任由它继续消耗CPU Cycles,直到发现需要的锁处于可用状态。 有了这一层了解,自旋锁的优势和劣势,以及其适用场景也就一目了然了。 优势: 没有线程阻塞,也就没有了线程上下文切换带来的开销 ...
自旋锁(Spin Lock)
转载请您注明出处: http://www.cnblogs.com/lsh123/p/7400625.html 0x01 自旋锁简介 自旋锁也是一种同步机制,它能保证某个资源只能被一个线程所拥有,这种保护被形象地称做“上锁”。它可以用于驱动程序中的同步处理。初始化自旋锁时,处理解锁状态,这时它可以被程序“获取”。“获取”后的自旋锁处理于锁定状态,不能再被“获取”。 如果自旋
Java中的自旋锁,手动实现一个自旋锁
CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,已达到锁的效果。自旋是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁, 当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
WINDOWS 自旋锁分析(二)
3 KeAcquireSpinLockAtDpcLevel的实现机制 MSDN上说明调用KeAcquireSpinLockAtDpcLevel的程序必须运行在DISPATCH_LEVEL上。 在多核处理器(Windows2003)下 先来查看一下KeAcquireSpinLockAtDpcLevel的汇编代码 nt!KeAcquireSpinLockAtDpcLevel: mov ecx,dword ptr [esp+4] nt!KeAcquireSpinLockAtDpcLevel+0x4 lock
从零构建AI辅助逆向分析环境:JADX-MCP与LLM的实战集成指南
本文详细介绍了如何从零构建AI辅助逆向分析环境,通过集成JADX-MCP与LLM技术,提升Android应用逆向工程效率。文章涵盖环境搭建、插件安装、MCP服务器配置及实战案例分析,帮助开发者快速掌握AI在代码理解、漏洞挖掘和自动化文档生成中的应用。
5分钟用AI搞定顶刊级引言!掌握三段式结构+避坑要点,让你的Introduction更有说服力(附提示词)
说明你是怎么来填补这个缺口的,一句话概括你的研究,再简述你采用了什么新方法、新模型或新视角,也就是创新点,最后说明你的方案是怎么克服第二段提到的缺口的,带来了哪些贡献,注意要确保方案与第二段提到的缺口一一对应。以上三段式引言模板,是非常经典的问题导向型结构,相比传统的结构,这种结构更能吸引是审稿人的注意,希望这篇分享的模板能给在你写论文引言时,提供一点帮助。上一段写好后,就在前人研究的基础上,亮出自己的方案,借助AI说明自己的研究,用了什么新方法/框架/理论/视角来填补这个缺口的。
WINDOWS 自旋锁分析(三)
4,KeAcquireSpinLockRaiseToDpc的实现机制 在MSDN上是这么说的:调用KeAcquireSpinLockRaiseToDpc的程序必须运行在IRQL<=DISPATCH_LEVEL 上 KeAcquireSpinLockRaiseToDpc和KeAcquireSpinLock具有同样的功能,但是比KeAcquireSpinLock具有更高的效率。 下面将着重关注KeAcquireSpinLockRaiseToDpc是如何提高效率的。 写到这里我打开Windbg准备看个究
【系统架构设计师】预测试卷一:综合知识(75道选择题)
软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式,架构风格反映领域中众多系统所共有的结构和 (48),强调对架构(49)的重用。 (48) A. 语义特性 B. 功能需求 C. 质量属性 D. 业务规则 (49) A. 分析 B. 设计 C.实现
Windows 自旋锁分析(一)
自旋锁是一种在内核定义,只能在内核态下使用的同步机制。自旋锁用来保护共享数据或者资源,使得并发执行的程序或者在高优先级IRQL的对称多处理器的程序能够正确访问这些数据。分析Windows自旋锁,首先需要介绍Windows的IRQL。 1 Interrupt Request Level(IRQL)介绍 IRQL是Interrupt RequestLevel,中断请求级别。一个由window
一个车载HUD的设计参考
一个车载HUD的设计参考
【WorkBuddy从入门到精通实战教程】实战案例 第 41 章 论文阅读与文献综述
本文系统讲解如何用WorkBuddy高效阅读论文并撰写文献综述。针对“读了一天没记住”的痛点,提出“三遍阅读法”:快速评估相关性、结构化提炼核心内容、批判性分析局限。结合提示词工程,实现单篇精读、多篇对比与综述框架搭建,显著提升效率。强调学术诚信,严防AI幻觉,要求所有引用与数据必须回源核对。最终目标是借助AI辅助理解与组织,但坚持自主思考与写作,真正掌握科研能力。
【原创+整理】线程同步之详解自旋锁
一 什么是自旋锁 自旋锁(Spinlock)是一种广泛运用的底层同步机制。自旋锁是一个互斥设备,它只有两个值:“锁定”和“解锁”。它通常实现为某个整数值中的某个位。希望获得某个特定锁得代码测试相关的位。如果锁可用,则“锁定”被设置,而代码继续进入临界区;相反,如果锁被其他人获得,则代码进入忙循环(而不是休眠,这也是自旋锁和一般锁的区别)并重复检查这个锁,直到该锁可用为止,这就是自旋的过程。“测试并...
Claude Code设置中文输出指南
Claude Code 的语言配置其实并不复杂,只要做好全局的默认设置,再加上一些小的提醒技巧,就能完全避免突然切换英文的问题,让你全程用中文流畅地和 AI 助手协作,不用再被语言问题打断工作节奏,真正提升开发效率。
自旋锁与适应性自旋锁
自旋锁与适应性自旋锁概念引入自旋锁与非自旋锁流程图自旋锁的缺陷自旋锁的实现原理自适应自旋锁 概念引入 在介绍自旋锁之前,我们需要介绍一些前提知识来帮助大家理解自旋锁的概念。 阻塞或唤醒一个Java进程,需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换花费的时间有可能比用户代码执行的时间还长。 在许多场景中,同步资源的锁定时间很短,为了这一小...
RT-DETR训练自己的数据集(从代码下载到实例测试)
在Windows10上配置CUDA环境教程是百度提出的一种实时目标检测模型。RT-DETR采用了与DETR相同的编码器和解码器结构,但对其进行了大量的优化,在保持较高检测精度的同时,实现了实时目标检测,为目标检测领域提供了一种新的有效解决方案,具有广泛的应用前景,如自动驾驶、智能监控、机器人等领域。以上是RT-DETR官方的论文和代码,详细介绍了RT-DETR的构成和实现。在YOLOv10中也实现了RT-DETR的代码,所以本文使用YOLOv10的项目文件进行RT-DETR的训练和测试。YOLOv10。
IEM模型,地表粗糙度模拟
利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟
Java中的自旋锁
java自旋锁简述及非公平锁和公平锁的实现
RU DOS版、EFI版32bit/64bit BIOS常用工具
DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。
非自旋锁VS自旋锁和适应性自旋锁
1、什么是自旋 所谓自旋,就是指当有另外一个线程来竞争锁时,这个线程会在原地循环等待,而不是把该线程给阻塞,直到那个获得锁的线程释放锁之后,这个线程就可以马上获得锁的。锁在原地循环的时候,是会消耗cpu的,就相当于在执行一个啥也没有的for循环。 自旋其实就是在当前这个线程获取同步资源锁失败的时候,该线程会在原地一直等待锁释放,不会把该线程阻塞,只要获得锁的那个线程释放锁之后,这个等待的线程马上就可以去获得锁。原地循环等待会占用处理器时间的,类似在执行一个空的for循环一样。 2、非自旋锁 非自旋锁尝试获取
STM32F407中文手册
STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。
Camera2Demo
在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作
Linux自旋锁
面对没有获取锁的现场,通常有两种处理方式。本文主要讲述自旋锁自旋锁其实是一种很乐观的锁,他认为只要再等一下下锁便能释放,避免了操作系统进程调度和线程切换。
什么是自旋锁
前言 阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间。如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长。 自旋锁 在有些场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。 如果机器有多个CPU核心,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁。 为了让当前线程“稍等一下”,我们需让当前线程进行自
自旋锁是什么
概述 spinlock又称自旋锁,是实现保护共享资源而提出的一种轻量级锁机制。 自旋锁与互斥锁比较类似,都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个持有者,即只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,而是一直循环在那里看是否该自旋锁的持有者已经释放了锁,"自旋"一词就是因此而得名。锁一旦被释放,就会被等待的线程立即获取,而不需要经过唤醒和上下文切换。 适用情况 自
自旋锁原理及java自旋锁
一、自旋锁的概念 首先是一种锁,与互斥锁相似,基本作用是用于线程(进程)之间的同步。与普通锁不同的是,一个线程A在获得普通锁后,如果再有线程B试图获取锁,那么这个线程B将会挂起(阻塞);试想下,如果两个线程资源竞争不是特别激烈,而处理器阻塞一个线程引起的线程上下文的切换的代价高于等待资源的代价的时候(锁的已保持者保持锁时间比较短),那么线程B可以不放弃CPU时间片,而是在“原地”忙等,直到锁的持
34、什么是自旋锁?以及使用自旋锁的好处和后果分别是什么呢?
首先,我们了解什么叫自旋?“自旋”可以理解为“自我旋转”,这里的“旋转”指“循环”,比如 while 循环或者 for 循环。“自旋”就是自己在这里不停地循环,直到目标达成。而不像普通的锁那样,如果获取不到锁就进入阻塞。
java锁结构之自旋锁与适应性自旋锁
java自旋锁机制
1906



被折叠的 条评论
为什么被折叠?



