(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 指令即可。
總結
以上是生活随笔為你收集整理的(68)自旋锁 , cmpxchg8b 指令的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (67)多核同步,lock 总线锁 ,自
- 下一篇: (69)番外 —— 编写一个简易的反调试