数据的同步
★.提升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
参考文章