26、Windows驱动程序的同步处理(2)

4、内核模式下的同步对象 

用户模式下用句柄来操作同步对象,而内核模式下可以获得同步对象的指针。每种同步对象在内核中均对应一种数据结构。 

1)等待 

KeWaitForMultipleObjects

KeWaitForSingleObject

如果超时则返回STATUS_TIMEOUT。如果是因为数组中其一个同步对象变为激发态,则函数的返回值减去STATUS_WAIT_0,就是激发的同步对象在数组中的索引号。

2)多线程

PsCreateSystemThread 包括用户线程与系统线程。用户线程属于当前进程(当前IO操作的发起者;如在IRP_MJ_READ的派遣函数中调用 PsCreateSystemThread创建用户线程,新线程属于调用ReadFile的进程)中的线程。系统线程一般是System线程。

创建的线程必须手动用PsTerminateSystemThread结束。PEPROCESS记录进程信息。

代码
1 #include "ntddk.h"
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

代码
1 void MyProcessThread(IN VOID pContext)
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 来使计数减一。

代码
1 int main()
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

代码
1 VOID MyProcessThread1(IN PVOID pContext)
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

代码
1 #include "windows.h"
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

整型自增互锁操作

原文地址:https://www.cnblogs.com/mydomain/p/1871167.html