unit MutualExclusion;
{$POINTERMATH ON}
interface
uses
nt_status, ntoskrnl, fcall;
function
_DriverEntry(pDriverObject:PDRIVER_OBJECT;
pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
implementation
const
{ 不能超过MAXIMUM_WAIT_OBJECTS (64) - 最大等待对象数 }
NUM_THREADS = 5;
NUM_WORKS = 10;
var
g_usDeviceName, g_usSymbolicLinkName: UNICODE_STRING;
g_pkWaitBlock: PKWAIT_BLOCK;
{ PKTHREAD数组 }
g_apkThreads: array[0..NUM_THREADS - 1] of PVOID;
g_dwCountThreads: DWORD;
g_kMutex: KMUTEX;
g_dwWorkElement: DWORD;
function
ThreadProc: NTSTATUS; stdcall;
var
liDelayTime:LARGE_INTEGER;
pkThread: PVOID; { PKTHREAD }
dwWorkElement: ULONG;
dwCount, dwTmp: ULONG;
begin
pkThread := PsGetCurrentThread;
DbgPrint(
'MutualExclusion: Thread %08X is entering ThreadProc'
dwCount := 0;
while
dwCount < NUM_WORKS
do
begin
DbgPrint(
'MutualExclusion: Thread %08X is working on #%d'
#13#10, pkThread, dwCount);
KeWaitForMutexObject(@g_kMutex, Executive, KernelMode,
False, nil);
{ 读取线程间共享资源的值 }
dwWorkElement := g_dwWorkElement;
{ 这里做些其他的事情 }
liDelayTime.HighPart := liDelayTime.HighPart or -1;
liDelayTime.LowPart := -(rand shl 4);
KeDelayExecutionThread(KernelMode,
false
, @liDelayTime);
{ 设置新的线程间共享资源的值 }
Inc(dwWorkElement);
g_dwWorkElement := dwWorkElement;
KeReleaseMutex(@g_kMutex, False);
dwTmp := (((ULONG(-liDelayTime.LowPart) * 3518437209) and $FFFF0000) shr 16) shr 13;
DbgPrint(
'MutualExclusion: Thread %08X work #%d is done (%02dms)'
#13#10,
pkThread, dwCount, dwTmp);
{ 计数器加一,做下一次循环 }
Inc(dwCount);
end;
DbgPrint(
'MutualExclusion: Thread %08X is about to terminate'
Result := PsTerminateSystemThread(STATUS_SUCCESS);
end;
procedure CleanUp(pDriverObject:PDRIVER_OBJECT);
begin
IoDeleteSymbolicLink(@g_usSymbolicLinkName);
IoDeleteDevice(pDriverObject^.DeviceObject);
if
g_pkWaitBlock <> nil
then
begin
ExFreePool(g_pkWaitBlock);
g_pkWaitBlock := nil;
end;
end;
procedure DriverUnload(pDriverObject:PDRIVER_OBJECT); stdcall;
begin
DbgPrint(
'MutualExclusion: Entering DriverUnload'
DbgPrint(
'MutualExclusion: Wait for threads exit...'
{ 因为ThreadProc存在于我们的驱动主体中,因此只要还有一个
{ 线程在运行,就不能卸载驱动,必须要等到所有的线程都退出 }
if
g_dwCountThreads > 0
then
begin
{ 没有设置超时时间,一直等待到所有线程退出 }
{ 因为有多个线程对象,所以使用KeWaitForMultipleObjects }
KeWaitForMultipleObjects(g_dwCountThreads, @g_apkThreads,
WaitAll, Executive, KernelMode,
False, nil, g_pkWaitBlock);
{ 执行到这里时,所有线程均已退出,就可以释放所有线程对象了 }
while
g_dwCountThreads > 0
do
begin
Dec(g_dwCountThreads);
ObfDereferenceObject(g_apkThreads[g_dwCountThreads]);
end;
end;
CleanUp(pDriverObject);
{ 打印结果. 这里g_dwWorkElement的值应该等于NUM_THREADS * NUM_WORKS }
DbgPrint(
'MutualExclusion: WorkElement = %d'
DbgPrint(
'MutualExclusion: Leaving DriverUnload'
end;
function
StartThreads: NTSTATUS;
var
hThread:HANDLE;
i, dwCount:ULONG;
rtnCode: NTSTATUS;
begin
i := 0;
{ dwCount保存实际运行的线程数 }
dwCount := 0;
while
i < NUM_THREADS
do
begin
{ 启动NUM_THREADS个线程 }
rtnCode := PsCreateSystemThread(@hThread, THREAD_ALL_ACCESS,
nil, 0, nil,
@ThreadProc, nil);
if
rtnCode = STATUS_SUCCESS
then
begin
{ 我们不需要PsCreateSystemThread返回的线程句柄. }
{ 但是我们需要指向它的指针. 以便我们引用线程对象并且关闭它. }
ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS,
nil, KernelMode,
@g_apkThreads[dwCount], nil);
ZwClose(hThread);
DbgPrint(
'MutualExclusion: System thread created. Thread Object: %08X'
g_apkThreads[dwCount]);
Inc(dwCount);
end
else
begin
DbgPrint(
'MutualExclusion: Can'
't create system thread. Status: %08X'
end;
Inc(i);
end;
g_dwCountThreads := dwCount;
if
dwCount <> 0
then
begin
Result := STATUS_SUCCESS; { 返回成功说明至少有一个线程在运行 }
end
else
begin
Result := STATUS_UNSUCCESSFUL; { 无法启动任何线程 }
end;
end;
function
_DriverEntry(pDriverObject:PDRIVER_OBJECT;
pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;
var
status:NTSTATUS;
pDeviceObject:PDEVICE_OBJECT;
liTickCount:LARGE_INTEGER;
begin
status := STATUS_DEVICE_CONFIGURATION_ERROR;
RtlInitUnicodeString(@g_usDeviceName,
'\Device\MutualExclusion'
);
RtlInitUnicodeString(@g_usSymbolicLinkName,
'\DosDevices\MutualExclusion'
);
if
IoCreateDevice(pDriverObject, 0, @g_usDeviceName,
FILE_DEVICE_UNKNOWN, 0, False,
@pDeviceObject) = STATUS_SUCCESS
then
begin
if
IoCreateSymbolicLink(@g_usSymbolicLinkName,
@g_usDeviceName) = STATUS_SUCCESS
then
begin
{ 因为ThreadProc存在于我们的驱动主体中,因此只要还有一个
{ 线程在运行,就不能卸载驱动,必须要等到所有的线程都退出
{ 方可卸载驱动。为了达到这个目的,我们需要一些内存。必须
{ 在这里分配这些内存,因为如果我们在DriverUnload中去分配,
{ 一旦分配失败,就没有办法停止驱动了。}
{ 每个线程对象都有一个内建的等待块(wait block)数组(缺省一
{ 个数组中有3个等待块)用于有几个对象并存时的等待操作。由于
{ 没有额外的等待块可供使用,因此通常情况下,这些内建的等待
{ 块数组被用于多个等待操作。当然,如果需要并存的对象的数目
{ 超过了内建等待块的数目, 可以通过设置WaitBlockArray参数指
{ 定一个替代的等待块用于等待操作。}
{ 在本例中,我们的NUM_THREADS大于THREAD_WAIT_OBJECTS(3). }
{ 因此就必须要使用自己的Wait Block了. }
g_pkWaitBlock := ExAllocatePool(NonPagedPool, NUM_THREADS * SizeOf(KWAIT_BLOCK));
if
g_pkWaitBlock <> nil
then
begin
{ 初始化mutex }
KeInitializeMutex(@g_kMutex, 0);
{ 出于性能方面的考虑, 可以使用Ex..FastMutex函数替代
{ Ke..Mutex. 当然, 快速mutex无法递归取得而内核mutex
{ 却可以;另一个缺点是ExAcquireFastMutex设置IRQL=APC_LEVEL,
{ 并且调用ExAcquireFastMutex返回后,调用者函数也将运行在
{ APC_LEVEL. }
KeQueryTickCount(@liTickCount);
{ 初始化随机数发生器种子 }
srand(liTickCount.LowPart);
g_dwWorkElement := 0;
if
StartThreads = STATUS_SUCCESS
then
begin
pDriverObject^.DriverUnload := @DriverUnload;
status := STATUS_SUCCESS;
end
else
begin
CleanUp(pDriverObject);
end;
end
else
begin
CleanUp(pDriverObject);
DbgPrint(
'MutualExclusion: Couldn'
't allocate memory for Wait Block'
end;
end
else
begin
IoDeleteDevice(pDeviceObject);
end;
end;
result := status;
end;
end.