1.事件对象
可以使用事件对象同步完成IRP,或者驱动线程间同步执行某些操作。
首先调用KeInitializeEvent IoCreateNotificationEvent或IoCreateSynchronizationEvent初始化事件。
流程:
驱动的一个线程调用KeWaitForSingleObject等待已初始化的事件
当操作完成后,该事件被置信号(调用KeSetEvent),等待线程进入准备执行状态。
内核执行线程分发,原本等待时间的线程开始执行。
2.信号灯对象
任何驱动都可以使用信号灯对象在驱动线程和处理例程之间同步I/O操作。
首先调用KeInitializeSemaphore 函数初始化信号灯。
流程:
驱动程序的一个线程调用KeWaitForSingleObject等待信号灯有信号。
当驱动程序接受到一个IRP请求后,相应处理例程将该IRP排入队列,并调用KeReleaseSemaphore函数增加一个信号灯,随后等待线程满足等待条件,准备执行。
线程分发,原等待线程开始执行。
3.互斥体对象
初始化:KeInitializeMutex
用来确保对共享资源互斥访问的同步机制。其中还有快速互斥体Fast Mutex和受限互斥体Guarded Mutex。互斥体可以递归请求(KeWaitForSingleObject)拥有对一个互斥体对象的所属权。只有完全释放 (KeReleaseMutex)所属权,互斥体对象才会有信号。
4.定时器对象
驱动程序可以使用定时器对象执行一个定时的操作。定时器对象包括DPC定时器和I/O定时器。IO定时器固定时间为1s,不可以改变。
首先调用KeInitializeTimer或KeInitializeTimerEx来初始化定时器。Ex函数比普通函数多一个TYPE参数,用 来指定DPC定时器响应类型是NotificationTimer(需要显式的设置为无信号。调用KeSetTimer)或者 SynchronizationTimer(自动变更为无信号状态)。KeInitializeTimer设置的为NotificationTimer。
1 #include "ntddk.h"
2
3 #ifdef __cplusplus
4 extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath);
5 #endif
6
7 void DPCDemoUnload(PDRIVER_OBJECT DriverObject);
8 VOID ThreadStart(PVOID StartContext);
9 VOID CustomDpc(struct _KDPC *Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2);
10
11 KTIMER Timer;
12
13 NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
14 {
15 HANDLE hThread;
16 OBJECT_ATTRIBUTES ObjectAttributes;
17 CLIENT_ID CID;
18 NTSTATUS status;
19
20
21 KdPrint(("[DriverEntry]DriverEntry Current Process:%s Current IRQL: %d\n", (char*)((ULONG)IoGetCurrentProcess()+0x174),KeGetCurrentIrql()));
22
23 //初始化定时器
24 KeInitializeTimer(&Timer);
25
26 DriverObject->DriverUnload = DPCDemoUnload;
27
28 InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
29
30 //创建一个系统线程
31 status = PsCreateSystemThread(&hThread,
32 GENERIC_READ | GENERIC_WRITE,
33 &ObjectAttributes,
34 NtCurrentProcess(),
35 &CID,
36 (PKSTART_ROUTINE)ThreadStart,
37 NULL);
38 if (!NT_SUCCESS(status))
39 {
40 KdPrint(("PsCreateSystemThread failed!\n"));
41 return 0;
42 }
43 ZwClose(hThread);
44 KdPrint(("Exit DriverEntry!\n"));
45 return STATUS_SUCCESS;
46 }
47
48 VOID ThreadStart(PVOID StartContext)
49 {
50 LARGE_INTEGER DueTime;
51 KDPC Dpc;
52
53 KdPrint(("Current Process: %s IRQL:%d\n", (char*)((ULONG)IoGetCurrentProcess()+0x174), KeGetCurrentIrql()));
54
55 DueTime = RtlConvertLongToLargeInteger(-1000000);
56
57 //初始化一个DPC
58 KeInitializeDpc(&Dpc, (PKDEFERRED_ROUTINE)CustomDpc, NULL);
59
60 //设置DPC定时器
61 KeSetTimer(&Timer, DueTime, &Dpc);
62
63 //等待定时器
64 KeWaitForSingleObject(&Timer, Executive, KernelMode, FALSE, NULL);
65
66 KdPrint(("ThreadStart time expire!\n"));
67
68 return;
69 }
70
71 VOID CustomDpc(struct _KDPC *Dpc, PVOID DefferedContext, PVOID SystemArgument1, PVOID systemArgument2)
72 {
73 KdPrint(("Current Process: %s IRQL:%d\n", (char*)((ULONG)IoGetCurrentProcess()+0x174), KeGetCurrentIrql()));
74
75 return;
76 }
77
78 void DPCDemoUnload(PDRIVER_OBJECT DriverObject)
79 {
80
81 return;
82 }
5.自旋锁
是一种内核模式专用的同步机制,可以在并行访问时保护共享数据。
当某个函数在获取自旋锁的过程中,不可以有如下行为:
触发硬件或者软件异常。
试图访问分页内存
可能造成死锁的递归或者把持自旋锁超过25微秒
调用可能引起以上三条的外部函数
使用KeInitialiezeSpinLock初始化自旋锁
使用KeAcquireSpinLock获取自旋锁
使用KeReleaseSpinLock释放自旋锁
KeAcquireInStackQueuedSpinLock和KeReleaseInStackQueuedSpinLock来获取和释放排队自旋锁。
6.回调对象
当某条件满足时,驱动程序A向驱动程序B通知某一消息。驱动程序A首先要创建一个回调对象,其他的驱动程序可以向该回调对象注册回调函数,当条件满足的时候,驱动程序A将调用所有像其注册的回调函数。
首先使用InitializeObjectAttributes初始化回调对象的属性,必须为回调对象赋名字,且Attributes参数必须设置OBJ_PERMANENT标记,然后调用ExCreateCallback函数创建回调对象。注册回调函数使用的函数是ExRegisterCallback。