内核中的同步机制(一)

时间:2021-02-19 15:46:57

数据的同步

★.提升IRQL实现同步

单核CPU上只要提升到DISPATCH_LEVEL就能实现数据的同步,因为线程不会切换。在多核CPU环境下,也有利用IRQL的提升来处理处理器相关的数据同步的必要。一个例子是线程调度时的同步处理,参考前面的文章~

★.利用cpu的原子操作执行,x86下的lock前缀对数据进行同步访问。例如lock xadd,xchg,lock cmpxchg,lock or lock xor lock and等等

单链表的同步也是使用64位的原子操作实现的, 

typedef union _SLIST_HEADER {
    ULONGLONG Alignment;
    struct {
        SLIST_ENTRY Next;
        WORD   Depth;
        WORD   Sequence;
    } DUMMYSTRUCTNAME;
} SLIST_HEADER, *PSLIST_HEADER;


Alignment就是原子操作要同步的数据,lock excmpchg8b 来尝试同步这个64位整数,不能获取就继续尝试,从而实现一个后进先出的同步单链表操作

★.自旋锁

内核态自旋锁,结构定义typedef ULONG_PTR KSPIN_LOCK;

实际就是一个状态,程序将锁包含造一个结构里面,每次访问这个结构时获取这个锁来达到同步

自旋锁的原理是将irql提升到DISPATCH_LEVEL,然后不断获取锁得状态,直到能够成功获取为止,单核下显然没啥意义,因为升到DISPATCH_LEVEL意味着线程不能调度…so…DeadLock….所以单核下获取锁只是简单的提升irql


自旋锁要求所有访问这个资源的代码都要事先获取这个锁,否则一个线程获取了这个锁,另一个线程却直接访问数据。。%¥&……¥

下面看一下 初始化 获取、释放锁的几个过程

KeInitializeSpinLock 简单的给锁赋值为0

#define KeAcquireSpinLock(a,b) *(b) = KfAcquireSpinLock(a)
#define KeReleaseSpinLock(a,b) KfReleaseSpinLock(a,b)

X86下 获取自旋锁的操作函数是在HAL中完成的,因为有单核多核之分,用的是不同的hal,一下调试信息来源于一篇文章,我的符号不知道怎么死活下载不下来了。。。。

hal!KfAcquireSpinLock:
    mov     edx,dword ptr ds:[0FFFE0080h]
    mov dword ptr ds:[0FFFE0080h],41h
    shr     edx,4
    movzx   eax,byte ptr hal!HalpVectorToIRQL [edx]
    ret


就是提升IRQL到DPC_LEVEL ,只要线程不切换,自然没有人跟我抢这个数据

多核下

hal!KfAcquireSpinLock:
    mov     eax,dword ptr fs:[00000024h] ;得到当前的IRQL
    mov     byte ptr fs:[24h],2           ; 提升到DISPATCH_LEVEL
    jmp     hal!KeAcquireSpinLockRaiseToSynch+0xe 
hal!KeAcquireSpinLockRaiseToSynch:
    mov     eax,dword ptr fs:[00000024h]
    mov     byte ptr fs:[24h],1Bh
hal!KeAcquireSpinLockRaiseToSynch+0xe
   lock bts dword ptr [ecx],0 ;原子方式进行比对锁的最后一位,
若是原来0置1返回
   jb      hal!KeAcquireSpinLockRaiseToSynch+0x16 若原来是1,则跳转
   ret
hal!KeAcquireSpinLockRaiseToSynch+0x16
   test    dword ptr [ecx],1    比对是否是1
   je      hal!KeAcquireSpinLockRaiseToSynch+0xe 不是1了跳转到0xe
   pause    否则休息一下
   jmp     hal!KeAcquireSpinLockRaiseToSynch+0x16 ;继续比对

说白了就是一个循环比对的操作,由于比较浪费资源(一直在打转,从未被切换),所以自旋锁只适合短时间内的同步 不宜长久等待

也有一些针对自旋锁优化的函数 
排队自旋锁,利用KPCR的LockQueue对锁进行排队,仅供系统内部使用
栈内自旋锁KeAcquireInStackQueuedSpinLock

原理都是一样的,详情可参考文末的参考链接

By ReturnsMe http://hi.baidu.com/andriy_aolala/blog/item/c4639b3ed12467ea828b13b9.html

参考文章

        明明白白自旋锁

        Windows 自旋锁分析