[转](68)自旋锁 , cmpxchg8b 指令

 

一、临界区和自旋锁的对比

 

上次课我们学习了临界区,并自己实现了一个。临界区是通过线程切换的方式来等待的,自旋锁则使用循环代替了线程切换,在多核环境下,使用自旋锁可以提高效率。

 

用一个例子来解释临界区和自旋锁的区别。只有一个厕所,有一个人进去了。

 

临界区就是外面的人过来看一眼发现没位子,就回家睡觉了,睡醒了再回来看看有没有位子,重复这样的步骤;

 

自旋锁就是外面人一看没位置,他就在原地打转,一有位子马上就进去了。

 

自旋锁的执行流程真的是在“旋转”,我觉得这个名字起得太贴切了。下面我们来看多核模式下自旋锁的实现。

 

二、KiAcquireSpinLock , KiReleaseSpinLock

 

下面是多核自旋锁实现。

 

解释一下 bts 指令,它是把 [ecx] 的第 0 位的值放到 CF,然后给第0 位置1. jb 指令看 CF=1就跳转。也就是说,如果原来第0位是1,就retn,进入临界区了;如果原来是1,那么 jb 跳转,开始“自旋”,直到 [ecx] 0位变成1,重新执行 KiAcquireSpinLock 。

 

自旋锁避免了线程切换,通过 pause 指令给CPU降温,非常漂亮的做法,但是单核模式下就不合适了。

 

.text:004699D0 @KiAcquireSpinLock@4 proc near          ; CODE XREF: InbvAcquireLock()+2D↑p
.text:004699D0                                         ; KdpPortLock()+5↑j ...
.text:004699D0                 lock bts dword ptr [ecx], 0
.text:004699D5                 jb      short loc_4699D8
.text:004699D7                 retn
.text:004699D8 ; ---------------------------------------------------------------------------
.text:004699D8
.text:004699D8 loc_4699D8:                             ; CODE XREF: KiAcquireSpinLock(x)+5↑j
.text:004699D8                                         ; KiAcquireSpinLock(x)+12↓j
.text:004699D8                 test    dword ptr [ecx], 1
.text:004699DE                 jz      short @KiAcquireSpinLock@4 ; KiAcquireSpinLock(x)
.text:004699E0                 pause
.text:004699E2                 jmp     short loc_4699D8
.text:004699E2 @KiAcquireSpinLock@4 endp
.text:004699E2
.text:004699E2 ; ---------------------------------------------------------------------------
.text:004699E4                 align 10h
.text:004699F0 ; Exported entry  50. KiReleaseSpinLock
.text:004699F0
.text:004699F0 ; =============== S U B R O U T I N E =======================================
.text:004699F0
.text:004699F0
.text:004699F0 ; __fastcall KiReleaseSpinLock(x)
.text:004699F0                 public @KiReleaseSpinLock@4
.text:004699F0 @KiReleaseSpinLock@4 proc near          ; CODE XREF: InbvReleaseLock()+E↑p
.text:004699F0                                         ; KdpPortUnlock()+5↑j ...
.text:004699F0                 mov     byte ptr [ecx], 0
.text:004699F3                 retn
.text:004699F3 @KiReleaseSpinLock@4 endp

 

三、在多核环境下,如何保证对一个高并发的内核函数进行HOOK而不会出错?写出你的代码。

 

这个问题的关键是,hook 后一般是 e8 / e9 后跟4字节,总共5字节,但没办法一次性改5个字节,可能改了第一个字节,正要改后4个字节时,别的线程进来了,就会出错。

 

我这介绍三种办法。

 

  • 短跳中转
  • 中断门
  • 找一条一次性修改8字节的指令

 

短跳中转是比较常用的,修改前2字节跳到某个长跳的方式,不多做介绍。

 

中断门也是只用改两个字节,需要先构造中断门,也不介绍。

 

本文重点介绍第三种,我以前没用过的方式,这个指令就是 cmpxchg8b .

 

cmpxchg8b 指令

 

cmpxchg8b mem64 指令的工作如下:
比较 mem64 和 EDX:EAX
如果相等,那么把 ECX:EBX 存储到 mem64
如果不相等,那么把 mem64 存储到 EDX:EAX

 

我们要一次性改8字节内存,用的是相等的情况,先把要写入的内容放到 ECX:EBX ,然后调用 cmpxchg8b 指令即可。


---------------------
作者:hambaga
来源:CSDN
原文:https://blog.csdn.net/Kwansy/article/details/109995196
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值