分类:
2011-05-14 21:41:53
驱动程序的同步处理
可重入,是指函数的执行结果不和执行顺序有关。同步机制很大程度上依赖于中断请求级。
IRQ编号 |
设备名称 |
用途 |
IRQ0 |
Tine |
计算机系统计时器 |
IRQ1 |
KeyBoard |
键盘 |
IRQ2 |
RedirectI RQ9 |
与IRQ9相接,MPU-401 MDI使用该IRQ |
IRQ3 |
COM2 |
串口设备 |
IRQ4 |
COM1 |
串口设备 |
IRQ5 |
LPT2 |
建议声卡使用该IRQ |
IRQ6 |
FDD |
软驱传输控制用 |
IRQ7 |
LPT1 |
打印机传输控制用 |
IRQ8 |
CMOSAlert |
即时时钟 |
IRQ9 |
RedirectI RQ2 |
与IRQ2相接。可设定给其他硬件使用 |
IRQ10 |
Reversed |
建议保留给网卡使用该IRQ |
IRQ11 |
Reversed |
建议保留给AGP显卡使用 |
IRQ12 |
PS/2 Mouse |
按PS/2鼠标,若无也可以设定给其他硬件使用 |
IRQ13 |
FPU |
协处理器用,例如FPU(浮点运算器) |
IRQ14 |
Primary IDE |
主硬盘传输控制用 |
IRQ15 |
Secondary lde |
从硬盘传输控制用 |
图 PIC中断向量 P220
APIC 高级PIC(Programmable interrupt controller),共24个中断。
1、中断请求级
1)基本概念
Windows 把中断请求级IRQL分成了32个,0~2级别为软件中断,3~31级为硬件中断。0~31优先级别逐步递增。硬件中断请求级别称为设备中断请求级 DIRQL。Windows大部分时间运行在软件中断请求级中。当设备中断来临时,操作系统提升IRQL到DIRQL,并运行中断处理函数。当中断处理函数结束后,操作系统把IRQL降到原来的级别。
图 IRQL P222
用户模式的代码是运行在最低优先级的passive_level;驱动程序的DriveEntry函数,派遣函数,AddDevice等函数一般都运行在PASSIVE_LEVEL级别,在必要时申请进入DISPATCH_LEVEL级别。
Windows 负责线程调度的组件运行在DISPATCH_LEVEL级别。当前线程运行完毕后,系统自动从PASSIVE_LEVEL提升到 DISPATCH_LEVEL级别,当切换完毕后,操作系统又从DISPATCH_LEVEL降回到PASSIVE_LEVEL级别。
驱动程序的StartIO函数和DPC(deferred procedure call)函数,中断服务例程也运行在DISPATCH_LEVEL级别。
线程运行在PASSIVE_LEVEL级别。如果提升到DISPATCH_LEVEL级别,则不会发生线程的切换,这是一种很常见的同步处理机制。
页故障允许在PASSIVE_LEVEL级别(出现页故障时,调用缺页机制,进行物理内存和磁盘文件进行切换),如果在DISPATCH_LEVEL或更高级别,则系统崩溃。对于DISPATCH_LEVEL或更高级别程序必须使用非分页内存。
2)控制IRQL提升与降低
主要用几个函数:
KeGetCurrentIrql
KeRaiseIrql
KeLowerIrql
2 {
3 KIRQL oldirql;
4 ASSERT(keGetCurrentIrql() <= DISPATCH_LEVEL);
5 keRaiseIrql(DISPATCH_LEVEL, &oldirql);
6 //...
7 kelowrIrql(oldirql);
8 }
示例程序 P224
2、自旋锁
同步处理机制。不同于线程中的等待事件;操作系统会把等待某一个事件的线程处于休眠状态,CPU运行其它线程;而自旋锁不会切换到其它线程,而是让这个线程一直自旋等待。所谓自旋,就是一直不停的询问:是否可以获取自旋锁。
在单CPU中,获取自旋锁仅仅是将当前的IRQL从PASSIVE_LEVEL级别升到DISPATCH_LEVEL级别,多CPU中要复杂一点。驱动程序必须在不大于DISPATCH_LEVEL级别中使用自旋锁。
自旋锁一般作用是为各派遣函数实现间同步。尽量不要把自旋锁放在全局变量中,而应当把自旋锁放在设备扩展里。
示例 参见http://www.cnblogs.com/mydomain/archive/2010/10/18/1855118.html
KeAcquireSpinLock
KeInitializeSpinLock
KeAcquireSpinLockAtDpcLevel
3、用户模式下的同步对象
同步对象包括事件,互斥体,信号灯等。用户模式下的同步对象是对内核模式下的同步对象的再封装。
1)等待
WaitForSingleObject
WaitForMultipleObjects
2)开启多线程
CreateThread
[1]中推荐_beginthread
%28VS.80%29.aspx
3)事件
典型的同步对象。
CreateEvent
所有形如CreateXXX的Win32 API,如果第一个参数是lpEventAttributes,则这种API内部都会创建一个相应的内核对象;这种API返回一个句柄(32位无符号整数),OS通过这个句柄找到具体的内核对象。
01 |
#include "windows.h" |
02 |
#include "process.h" |
03 |
#include "stddef.h" |
04 |
#include "conio.h" |
05 |
|
06 |
UNIT WINAPI Thread1( LPVOID para) |
07 |
{ |
08 |
printf ( "Enter Thread\n" ); |
09 |
HANDLE *phEvent = ( HANDLE *)para; |
10 |
SetEvent(*phEvent); |
11 |
printf ( "Leave Thread1\n" ); |
12 |
return 0; |
13 |
} |
14 |
|
15 |
int main() |
16 |
{ |
17 |
HANDLE hEvent = CreateEvent(NULL, 0, FALSE, NULL); |
18 |
HANDLE hThread1 = ( HANDLE )_beginthreadex(NULL, 0, Thread1, &hEvent, 0, NULL); |
19 |
WaitForSingleObject(hEvent, INFINITE); |
20 |
return 0; |
21 |
} |
示例代码 P228
4)信号灯
常见的同步对象。
一般同步对象有两种状态:激发状态和未激发状态。
信号灯有个计数器,代表N个灯,只要有一个灯亮着,就说明处于激发状态。
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // SD
LONG lInitialCount, // initial count
LONG lMaximumCount, // maximum count
LPCTSTR lpName // object name
);
lInitialCount ,如果初始值大于0,则处理激发状态。
ReleaseSemaphore
对信号灯执行一次等待操作,减少一个读数,相当于熄灭一个灯泡。
2 #include "process.h"
3 #include "stdio.h"
4
5 UINT WINAPI Thread1(LPVOID para)
6 {
7 printf("Enter Thread1\n");
8 HANDLE *hSemaphore = (HANDLE*)para;
9 Sleep(5000);
10 printf("Leave Thread1\n");
11 ReleaseSemaphore(*hSemaphore, 1, NULL);
12 return 0;
13 }
14 int main()
15 {
16 HANDLE hSemaphore = CreateSemaphore(NULL, 2, 2, NULL);
17 WaitForSingleObject(hSemaphore, INFINITE);
18 WaitForSingleObject(hSemaphore, INFINITE);
19 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hSemaphore, 0, NULL);
20 WaitForSingleObject(hSemaphore, INFINITE);
21 }
示例代码 P229
5)互斥体
用互斥体来避免多个线程使用一个资源。互斥体类似于同步事件;不同的是同一个线程可以递归获得互斥体,而同步事件不能。
如果线程获得互斥体时,互斥体此时的状态是未激发(nonsignaled),而释放互斥体时,互斥体的状态为激发态。
激发有点拗口,就是signaled,也就是接到了信号了。激发与未激发,都是相对于其它线程而言的。
CreateMutex
2 #include "process.h"
3 #include "stdio.h"
4
5 UINT WINAPI Thread1(LPVOID para)
6 {
7 HANDLE *phMutex = (HANDLE*)para;
8 WaitForSingleObject(phMutex, INFINITE);
9 WaitForSingleObject(phMutex, INFINITE);//对于同一个线程,可以获得多次
10
11 printf("Enter Thread1\n");
12 Sleep(5000);
13 printf("Leave Thread1\n");
14 ReleaseMutex(*phMutex);
15 return 0;
16 }
17
18 UINT WINAPI Thread2(LPVOID para)
19 {
20 HANDLE *phMutex = (HANDLE*)para;
21 WaitForSingleObject(phMutex, INFINITE);
22 WaitForSingleObject(phMutex, INFINITE);//对于同一个线程,可以获得多次
23
24 printf("Enter Thread2\n");
25 Sleep(5000);
26 printf("Leave Thread2\n");
27 ReleaseMutex(*phMutex);
28 return 0;
29 }
30
31 int main()
32 {
33 HANDLE hMutex = CreateMutex(NULL, NULL, NULL);
34 WaitForSingleObject(hSemaphore, INFINITE);
35 WaitForSingleObject(hSemaphore, INFINITE);
36 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hMutex, 0, NULL);
37 HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread2, &hMutex, 0, NULL);
38 Sleep(5000);
39 return 0;
40 }
示例代码 P230
6)等待线程完成
线程对象。每个对象同样有两种状态:运行之中时为未激发,当终止时为激发状态。
2 #include "process.h"
3 #include "stdio.h"
4
5 UINT WINAPI Thread(LPVOID para)
6 {
7 printf("Enter Thread2\n");
8 Sleep(5000);
9 return 0;
10 }
11
12 int main()
13 {
14 HANDLE hThread[2];
15 WaitForSingleObject(hSemaphore, INFINITE);
16 WaitForSingleObject(hSemaphore, INFINITE);
17 hThread[0] = (HANDLE)_beginthreadex(NULL, 0, Thread, &hMutex, 0, NULL);
18 hThread[1] = (HANDLE)_beginthreadex(NULL, 0, Thread, &hMutex, 0, NULL);
19 WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
20 return 0;
21 }
示例代码 P232
4、内核模式下的同步对象
用户模式下用句柄来操作同步对象,而内核模式下可以获得同步对象的指针。每种同步对象在内核中均对应一种数据结构。
1)等待
KeWaitForMultipleObjects
KeWaitForSingleObject
如果超时则返回STATUS_TIMEOUT。如果是因为数组中其一个同步对象变为激发态,则函数的返回值减去STATUS_WAIT_0,就是激发的同步对象在数组中的索引号。
2)多线程
PsCreateSystemThread 包括用户线程与系统线程。用户线程属于当前进程(当前IO操作的发起者;如在IRP_MJ_READ的派遣函数中调用 PsCreateSystemThread创建用户线程,新线程属于调用ReadFile的进程)中的线程。系统线程一般是System线程。
创建的线程必须手动用PsTerminateSystemThread结束。PEPROCESS记录进程信息。
2 VOID SystemThread(IN PVOID pContext)
3 {
4 KdPrint(("Enter SystemThread\n"));
5 PEPROCESS pEProcess = IoGetCurrentProcess();
6 PTSTR ProcessName = (PTSTR)((ULONG)pEProcess + 0x174);
7 KdPrint(("this thread is run in %s process!\n", ProcessName));
8 KdPrint(("Leave SystemThread\n"));
9 PsTerminateSystemThread (NT_STATUS);
10 }
11
12 VOID MyProcessThread(IN PVOID pContext)
13 {
14 KdPrint(("Enter MyProcessThread\n"));
15 PEPROCESS pEProcess = IoGetCurrentProcess();
16 PTSTR ProcessName = (PTSTR)((ULONG)pEProcess + 0x174);
17 KdPrint(("this thread is run in %s process!\n", ProcessName));
18 KdPrint(("Leave MyProcessThread\n"));
19 PsTerminateSystemThread (NT_STATUS);
20 }
21
22 void CreateThread_Test()
23 {
24 HANDLE hSystemThread, hMyThread;
25 NTSTATUS status = PsCreateSystemThread(&hSystemThread, 0, NULL, NULL, NULL, SystemThread, NULL);
26 NTSTATUS status = PsCreateSystemThread(&hMyThread, 0, NULL, NULL, NULL, MyProcessThread, NULL);
27 }
示例代码 P236
3)内核模式下的事件对象
KEVENT
KeInitializeEvent
2 {
3 PKEVENT pEvent = (PKEVENT)pContext;
4 kdPrint(("Enter MyProcessThread\n"));
5 KeSetEvent(pEvent, IO_NO_INCREMENT, FALSE);
6 kdPrint(("Leave MyProcessThread\n"));
7 PsTerminateSystemThread (NT_STATUS);
8 }
9 void Test()
10 {
11 HANDLE hMyThread;
12 KEVENT kEvent;
13 KeInitializeEvent(&kEvent, NotificationEvent, FALSE);
14 NTSTATUS status = PsCreateSystemThread(&hMyThread, 0, NULL, NtCurrentProcess(), NULL, MyProcessThread, &kEvent);
15 keWaitforSingleobject(&kEvent, Executive, KernalMode, FALSE, NULL);
16 }
示例代码 P237
4)驱动程序与应用程序交互事件对象
在用户模式下创建一个同步事件,然后用DeviceIoControl把事件句柄传递给驱动程序。句柄是与进程相关的,一个进程中的句柄只能在这个进程中有效,句柄相当于事件对象在进程中的索引。DDK中提供了如下函数:
ObReferenceObjectByHandle
ObDereferenceObject
ObReferenceObjectByHandle 在得到指针的同时,会为对象的指针维护一个计数,每次调用ObReferenceObjectByHandle会使计数加1;为使计数平衡,应当在后面适当时候调用ObDereferenceObject 来使计数减一。
2 {
3 HANDLE hDevice = CreateFile("\\\\.\\HelloDDK",
4 GENERIC_READ |GENERIC_WRITE,
5 0,
6 NULL,
7 OPEN_EXISTING,
8 FILE_ATTRIBUTE_NORMAL,
9 NULL);
10 if (hDevice == INVALID_HANDLE_VALUE)
11 {
12 printf("Failed to obtain file handle to device:%s with win32 error code:%d\n","MYWDMDevice", GetLastError());
13 return 1;
14 }
15 bool bRet;
16 DWORD dwOutput;
17 HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
18 //建立辅助线程
19 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hEvent, 0, NULL);
20 //将用户模式下的事件句柄传递给驱动
21 //IOCTL_TRANSMIT_EVENT是自己定义的CODE
22 bRet = DeviceIoControl(hDevice,
23 IOCTL_TRANSMIT_EVENT,
24 &hEvent,
25 sizeof(hEvent),
26 NULL,
27 0,
28 &dwOutput,
29 NULL);
30 WaitForSingleObject(hThread1, INFINITE);
31 CloseHandle(hDevice);
32 CloseHandle(hThread1);
33 CloseHandle(hEvent);
34 return 0;
35 }
36
37 NTSTATUS HelloDDKDeviceIoControl(IN PDEVICE_OBJECT pDevObj,
38 IN PIRP pIrp)
39 {
40 NTSTATUS status = STATUS_SUCESS;
41 KdPrint(("Enter HelloDDKDeviceIoControl\n"));
42 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
43 //获得输入参数大小
44 ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
45 //获得输出参数大小
46 ULONG cbout = stack->Parameters.DeviceIoControl.OutPutBufferLength;
47 //获得IOCTL码
48 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
49 ULONG info = 0;
50 switch (code)
51 {
52 case IOCTL_TRANSMIT_EVENT:
53 {
54 //得到应用程序传进来的事件
55 HANDLE hUserEvent = *(HANDLE*)pIrp->AssociatedIrp.SystemBuffer;
56 PKEVENT pEvent;
57 status = ObReferenceObjectByHandle(hUserEvent, EVENT_MODIFY_STATE, *ExEventObjecttype,
58 kernalmode,
59 (PVOID*)&pEvent, NULL);
60 keSetEvent(pEvent, IO_NO_INCREMENT, FALSE);
61 ObDereferenceObject(pEvent);
62 break;
63 }
64 default:
65 status = STATUS_INVALID_PARAMETER;
66 }
67 pIrp->IoStatus.Status = status;
68 pIrp->IoStatus.Information = info;
69 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
70 return status;
71 }
示例代码 P238
5)驱动程序与驱动程序间交互事件对象
最简单的方法是创建一个有名字的事件对象,这样在另一个驱动程序中就可以根据名字来寻找到事件对象的指针。
6)内核模式下的信号灯
用户模式下,信号灯对象用句柄表示,在内核模式下,用ksemaphore结构表示。
KeInitializeSemaphore
KeReadStateSemaphore
KeReleaseSemaphore 释放信号灯会增加计数
KeWaitXXX可获取信号灯,如果成功获得,计数减一,否则等待。
示例代码 P240,基本原理同上,略
7)内核模式下的互斥体
KeInitializeMutex
KeReleaseMutex
2 {
3 PKMUTEX pkMutex = (PKMUTEX)pContext;
4 keWaitforSingleobject(pkMutex, Executive, KernalMode, FALSE, NULL);
5 kdPrint(("Enter MyProcessThread1\n"));
6 //停止50ms
7 KeStallExecutionProcessor(50);
8 kdPrint(("Leave MyProcessThread1\n"));
9 KeReleaseMutex(pkMutex, FALSE);
10 PsTerminateSystemThread(STATUS_SUCCESS);
11 }
12
13 VOID MyProcessThread2(IN PVOID pContext)
14 {
15 PKMUTEX pkMutex = (PKMUTEX)pContext;
16 keWaitforSingleobject(pkMutex, Executive, KernalMode, FALSE, NULL);
17 kdPrint(("Enter MyProcessThread2\n"));
18 //停止50ms
19 KeStallExecutionProcessor(50);
20 kdPrint(("Leave MyProcessThread2\n"));
21 KeReleaseMutex(pkMutex, FALSE);
22 PsTerminateSystemThread(STATUS_SUCCESS);
23 }
24 #pragma PAGEDCODE
25 VOID Test()
26 {
27 HANDLE hMyThread1, hMyThread2;
28 KMUTEX hMutex;
29 KeInitializeMutex(&hMutex, 0);
30 NTSTATUS status = PsCreateSystemThread(&hMyThread1, 0, NULL, NtCurrentProcess(), NULL, MyProcessThread1, &hMutex);
31 NTSTATUS status2 = PsCreateSystemThread(&hMyThread2, 0, NULL, NtCurrentProcess(), NULL, MyProcessThread2, &hMutex);
32 PVOID Pointer_Array[2];
33 ObReferenceObjectByHandle(hMyThread1, 0, NULL,
34 kernalmode,
35 Pointer_Array[0],
36 NULL);
37 ObReferenceObjectByHandle(hMyThread2, 0, NULL,
38 kernalmode,
39 Pointer_Array[1],
40 NULL);
41 KeWaitForMultipleObjects(2, Pointer_Array, WaitAll, Executive, KernelMode,
42 FALSE, NULL, NULL);
43 ObDereferenceObject(Pointer_Array[0]);
44 ObDereferenceObject(Pointer_Array[1]);
45 kdPrint(("After KeWaitForMultipleObjects\n"));
46 }
示例代码 P242
8)快速互斥体
比普通互斥体快。不过不能递归获取互斥体对象。普通互斥体数据结构是MUTEX,快带互斥体是FAST_MUTEX。
示例代码 P244 基本原理同上,只是数据结构变了,此略
4、其他同步方法
1)使用自旋锁
KeInitializeSpinLock
KeReleaseSpinLock
2 #include "process.h"
3 #include "stdio.h"
4 #include "winioctl.h"
5 #define "..\NT_Driver\Ioctls.h"
6
7 UINT WINAPI Thread1(LPVOID pContext)
8 {
9 BOOL bRet;
10 DWORD dwOutput;
11 bRet = DeviceIoControl(*(PHANDLE)pContext,
12 IOCTL_MYDEFINITION,
13 NULL,
14 0,
15 NULL,
16 0,
17 &dwOutput,
18 NULL);
19 return 0;
20 }
21
22 int main()
23 {
24 HANDLE hDevice = CreateFile("\\\\.\\HelloDDK",
25 GENERIC_READ |GENERIC_WRITE,
26 0,
27 NULL,
28 OPEN_EXISTING,
29 FILE_ATTRIBUTE_NORMAL,
30 NULL);
31 if (hDevice == INVALID_HANDLE_VALUE)
32 {
33 printf("Failed to obtain file handle to device:%s with win32 error code:%d\n","MYWDMDevice", GetLastError());
34 return 1;
35 }
36 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hDevice, 0, NULL);
37 HANDLE hThread2 = (HANDLE)_beginthreadex(NULL, 0, Thread1, &hDevice, 0, NULL);
38 WaitForMultipleObjects(2, hThread, TRUE, INFINITE);
39 CloseHandle(hThread1);
40 CloseHandle(hThread2);
41 CloseHandle(hDevice);
42 return 0;
43 }
44
45 NTSTATUS HelloDDKDeviceIoControl(IN PDEVICE_OBJECT pDevObj,
46 IN PIRP pIrp)
47 {
48 ASSERT(KeGetCurrentIrpl() == PASSIVE_LEVLE));
49 PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
50 KIRQL oldirql;
51 KeAcquireSpinLock(&pdx->My_SpinLock, &oldirql);
52 //...开始
53 ASSERT(KeGetCurrentIrpl() == DISPATCH_LEVEL));
54 pIrp->IoStatus.Status = STATUS_SUCCESS;
55 pIrp->IoStatus.information = 0;
56 IoCompleteRequest(pIrp, IO_NO_INCREMENT);
57 NTSTATUS status = STATUS_SUCESS;
58 //...结束
59 KeReleaseSpinLock(&pdx->My_SpinLock, oldirql);
60 return status;
61 }
示例代码 P245
2)互锁操作进行同步
在多线程操作中,对于全局变量的自增、自减等操作,可能不同时候操作,结果是不一样的。因为一个自增等操作翻译成汇编是多条语句,多个线程访问导致了不可重入性。解决方法一是通过自旋锁等同步操作,另外一个方法,便是下面介绍的互锁操作。
保证操作的原子性。DDK提供了如下函数:
InterlockedXX
ExInterlockedXX
1 int number = 0;
2 void Foo()
3 {
4 KeAcquireSpinLock(...)
5 number++;
6 KeReleaseSpinLock(...)
7 }
8
9 int number2 = 0;
10 void Foo2()
11 {
12 InterlockedIncrement(&number2);//原子方式的自增
13 }
前者不通过自旋锁实现,内部不会提升IRQL,可以操作非分页数据和分页数据;后者是通过自旋锁实现的,需要程序员提供一个自旋锁,不能操作分页内存的数据。
ExInterlockedXX
内核函数 |
功能 |
ExInterlockedAddLargeInteger |
64位整数加法互锁操作 |
ExInterlockedAddLargeStatistic |
64位整数加法互锁操作 |
ExInterlockedAddUlong |
32位整数加法互锁操作 |
ExInterlockedAllocateFromZone |
分配互锁操作 |
ExInterlockedCompareExchange64 |
两个32位整数互换互锁操作 |
ExInterlockedDecrementLong |
32位整数减法互锁操作 |
ExInterlockedExchangeAddLargeInteser |
64为整数加法互锁操作 |
ExInterlockedExchangeUlong |
两个整数互换互锁操作 |
ExInterlockedFlushSList |
删除链表全部元素的互锁操作 |
ExInterlockedIncrementLong |
32位整数自增互锁操作 |
ExInterlockedInsertHeadList |
插入双向链表互锁操作 |
ExInterlockedInsertTailList |
插入双向链表互锁操作 |
ExInterlockedPopEntryList |
删除单向链表互锁操作 |
ExInterlockedPopEntrySList |
删除单向链表互锁操作 |
ExInterlockedPushEntryList |
插入单向链表互锁操作 |
ExInterlockedPushEntrySList |
插入单向链表互锁操作 |
ExInterlockedRemoveHeadList |
插入双向链表互锁操作 |
InterlockedXX
内核函数 |
功能 |
InterlockedCompareExchange |
比较互锁操作 |
InterlockedCompareExchangePointer |
比较互锁操作 |
InterlockedDecrement |
整型自减互锁操作 |
InterlockedExchange |
整型交换互锁操作 |
InterlockedExchangeAdd |
两个整型相加互锁操作 |
InterlockedExchangePinter |
为指针赋值互锁操作 |
InterlockedIncrement |
整型自增互锁操作 |