Windows Driver Model >> C/C++ >> Synchronization
Semaphore
參考資訊:
1. Source Code
Semaphore可以想像成是集成多個Mutex的機制,Semaphore可以設定取得Lock的數量,不像Mutex只有單一個Lock限制,因此,如果程式區段有執行數量的限制時,Semaphore是一個相當適合的方式,值得注意的是,釋放Semaphore不能超過設定的數量,否則會有STATUS_SEMAPHORE_LIMIT_EXCEEDED,使用步驟如下:
1. KeInitializeSemaphore()
2. KeWaitForSingleObject()
3. KeReleaseSemaphore()
main.c
#include <wdm.h> #define DEV_NAME L"\\Device\\MyDriver" #define SYM_NAME L"\\DosDevices\\MyDriver" #define MAX_THREAD 3 #define MAX_SEMA_COUNT 2 #define MAX_SEMA_LIMIT 2 KSEMAPHORE mySemaphore={0}; PVOID pThread[MAX_THREAD]={0}; PDEVICE_OBJECT pNextDevice=NULL; void MyThread(PVOID pParam) { ULONG cc=0; int t=(int)pParam; NTSTATUS status=0; LARGE_INTEGER stTime; stTime.HighPart|= -1; stTime.LowPart = -10000000; DbgPrint("Thread%d, Acquiring Semaphore", t); status = KeWaitForSingleObject(&mySemaphore, Executive, KernelMode, FALSE, NULL); DbgPrint("Thread%d, Acquired Semaphore", t); DbgPrint("Thread%d, Sleeping", t); KeDelayExecutionThread(KernelMode, FALSE, &stTime); DbgPrint("Thread%d, Releasing Semaphore", t); cc = KeReadStateSemaphore(&mySemaphore); KeReleaseSemaphore(&mySemaphore, IO_NO_INCREMENT, (cc < MAX_SEMA_LIMIT) ? 1 : 0, FALSE); DbgPrint("Thread%d, Released Semaphore", t); PsTerminateSystemThread(STATUS_SUCCESS); } NTSTATUS AddDevice(PDRIVER_OBJECT pOurDriver, PDEVICE_OBJECT pPhyDevice) { PDEVICE_OBJECT pOurDevice=NULL; UNICODE_STRING usDeviceName; UNICODE_STRING usSymboName; RtlInitUnicodeString(&usDeviceName, DEV_NAME); IoCreateDevice(pOurDriver, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pOurDevice); RtlInitUnicodeString(&usSymboName, SYM_NAME); IoCreateSymbolicLink(&usSymboName, &usDeviceName); pNextDevice = IoAttachDeviceToDeviceStack(pOurDevice, pPhyDevice); pOurDevice->Flags&= ~DO_DEVICE_INITIALIZING; pOurDevice->Flags|= DO_BUFFERED_IO; return STATUS_SUCCESS; } void Unload(PDRIVER_OBJECT pOurDriver) { pOurDriver = pOurDriver; } NTSTATUS IrpPnp(PDEVICE_OBJECT pOurDevice, PIRP pIrp) { int cc=0; UNICODE_STRING usSymboName={0}; PIO_STACK_LOCATION psk = IoGetCurrentIrpStackLocation(pIrp); if(psk->MinorFunction == IRP_MN_REMOVE_DEVICE){ for(cc=0; cc<MAX_THREAD; cc++){ if(pThread[cc] != NULL){ KeWaitForSingleObject(pThread[cc], Executive, KernelMode, FALSE, NULL); ObDereferenceObject(pThread[cc]); } } RtlInitUnicodeString(&usSymboName, SYM_NAME); IoDeleteSymbolicLink(&usSymboName); IoDetachDevice(pNextDevice); IoDeleteDevice(pOurDevice); } IoSkipCurrentIrpStackLocation(pIrp); return IoCallDriver(pNextDevice, pIrp); } NTSTATUS DriverEntry(PDRIVER_OBJECT pOurDriver, PUNICODE_STRING pOurRegistry) { int cc=0; HANDLE hThread=0; NTSTATUS status=0; pOurDriver->MajorFunction[IRP_MJ_PNP] = IrpPnp; pOurDriver->DriverExtension->AddDevice = AddDevice; pOurDriver->DriverUnload = Unload; KeInitializeSemaphore(&mySemaphore, MAX_SEMA_COUNT, MAX_SEMA_LIMIT); for(cc=0; cc<MAX_THREAD; cc++){ status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, (PHANDLE)-1, NULL, MyThread, (PVOID)cc); if(status == STATUS_SUCCESS){ ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL, KernelMode, &pThread[cc], NULL); ZwClose(hThread); } } return STATUS_SUCCESS; }
DriverEntry()產生3個Thread,每個Thread會嘗試取得Semaphore,一旦取得Semaphore後,會延遲一秒後才釋放Semaphore,而Semaphore的數量設定成2,因此,每次最多只能有兩個Thread取得Semaphore
結果