同步对于计算机系统来说是一个必须要面对的问题, windows系统对内核或外部应用程序提供了多种同步机制,通过这篇文章来介绍
- 同步机制的工作机制
- 同步机制的使用方法
同步机制可以按照不同的方式分类,如按照IRQL的高低,按照内核模式还是用户模式,按照是否可以跨进程等。在这里我们按照是否升高IRQL来将同步机制分类。
高IRQL同步
系统提供的同步机制必须要保证一点,就是在任意时刻只能由一个处理器在一个需要同步的关键段执行,那在这个过程中最大的问题来自于系统中断,如果处理器在执行关键段的时候被中断打扰,就无法保证同步。CPU可以通过提升IRQL的方式来屏蔽中断,但是这种方式只适用于单个内核,因为每个内核会有一个IRQL。对于多核处理器不仅需要提升IRQL,同时还要采用其他方式避免其他内核同时对关键段的访问。
Interlocked operations
工作机制
Interlocked operations提供了一系列的方法可以原子性的对变量进行更改操作。其实现方式是通过硬件支持的指令锁住处理器总线来避免其他处理器访问。
通过debug命令可以查看该系列方法的汇编代码。
0:010> uf kernel32!InterlockedIncrementkernel32!InterlockedIncrement:
75 7619efc0 8b4c2404 mov ecx,dword ptr [esp+4]
78 7619efc4 b801000000 mov eax,1
79 7619efc9 f00fc101 lock xadd dword ptr [ecx],eax
80 7619efcd 40 inc eax
81 7619efce c20400 ret 4
这类方法在windows编程中经常会被用到,例如CLR对于对象头的同步块处理中去设置某些位的值,由于需要多线程同步,就是通过调用类似的方法实现。
使用方法
同时C#中也包装了类似的方法
http://msdn.microsoft.com/en-us/library/system.threading.interlocked.aspx
Spinlocks
工作机制
Spinlocks是一种内核在访问内部数据结构(例如DPC Queue)的时候使用,它与其要保护的数据结构一样都是在non-paged pool内存中分配。他提供的同步方式是,当线程尝试去得到锁的过程中如果锁被其他线程占用,则不停的循环访问该锁,直到得到为止。这种机制一般用在等待时间很短的情况下,否则这种忙等待机制会给CPU增加非常大的负担。
它的实现也是通过硬件支持的test-and-set指令实现。与interlocked operations效果类似,锁住处理器总线避免其他处理器同时访问,另外也要提升IRQL避免可能的中断打扰。
使用方法
C#中提供了类似的实现
http://msdn.microsoft.com/en-us/library/system.threading.spinlock.aspx
Queued spinlocks
工作机制
相较常规的spinlock一种更加常用的spinlock叫做queued spinlock。根据名字可以推测应该有一个queue与spinlock相关联。当处理器请求一个被锁住的spinlock时,处理器把自己的标识放在spinlock相关联的queue中,当拿着锁的处理器释放该锁时,他会把锁的拥有权交给queue中的一个排队的处理器。这样处理器就不需要去查看锁的状态而只需要查看当前处理器的一个标志位来判断是否轮到了自己。
Queued spinlock与常规的spinlock有两点不同,
第一是多处理器总线不会被频繁的访问
第二是queued spinlock对于锁的取得是有顺序的
使用方法
Windows定义了一定数量的queued spinlocks,可以通过调用KeAcquireQueuedSpinLock来获得。注意这种方式只是给windows内核自己使用的。第三方的开发者无法使用这种方式。但是不要失望,接下来就是我们可以采用的方式。
Instack queued spinlocks
工作机制
驱动开发者可以使用动态分配的queued spinlocks,系统组件中例如cache manager,executivepool manager, NTFS都用到了这种方式。因为相较于windows的静态queued spinlocks动态分配的spinlock需要在stack上保存spinlock的handle以及相应的queuehandle,所以我们称它为instack queuedspinlocks。
使用方法
Instac