我之前写过一篇文章:https://blog.csdn.net/dailongjian2008/article/details/78621980,讲述如何解决在DISPATCH_LEVEL下读写文件的问题,我当时的做法是将IRQL强行降至PASSIVE_LEVEL,虽然软件发布后运行一直稳定,此处没有出现过任何问题,但是总感觉不是很安心,因为是采用强制的方式,会不会影响其他层面的调度却是一个未知数。
接下来还有一种方式能解决这样的问题,就是WorkItem的方式,windows系统会执行一个Worker线程,该线程会不断从Worker队列中取出任务然后执行,执行是在PASSIVE_LEVEL下的,所以我们可以将我们要进行的文件操作作为一个WorkerItem放入Worker队列中,以下是我在IoControl中执行设置EDID的操作,该操作需要在PASSIVE_LEVEL下执行。
static NTSTATUS HandleIoControl(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp) { PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); KIRQL OldIrql; if (irpsp->MajorFunction != IRP_MJ_DEVICE_CONTROL) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return STATUS_SUCCESS; } PIO_WORKITEM pIoWorkItem = IoAllocateWorkItem(pDevObj); if (pIoWorkItem == nullptr) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; IoCompleteRequest(pIrp,IO_NO_INCREMENT); return STATUS_INSUFFICIENT_RESOURCES; } KeAcquireSpinLock(&g_lock,&OldIrql); IoInitializeWorkItem(pDevObj,pIoWorkItem); IoQueueWorkItemEx(pIoWorkItem,(PIO_WORKITEM_ROUTINE_EX)DoIocontrol,DelayedWorkQueue,pIrp); IoMarkIrpPending(pIrp); KeReleaseSpinLock(&g_lock,OldIrql); return STATUS_PENDING; }
其中 DoIoControl是Worker线程要执行的任务,pIrp是传递给DoIoControl的一个参数,该参数类型可自定义。看下DoIoControl的实现:
static VOID DoIocontrol(IN PDEVICE_OBJECT DeviceObject,PIRP pIrp,PIO_WORKITEM IoWorkItem) { UNREFERENCED_PARAMETER(DeviceObject); KIRQL OldIrql; KeAcquireSpinLock(&g_lock,&OldIrql); NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION irpsp = IoGetCurrentIrpStackLocation(pIrp); pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = status; switch (irpsp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SET_CUSTOM_EDID: { ULONG uInputLength = irpsp->Parameters.DeviceIoControl.InputBufferLength; BYTE *inputBuffer = (BYTE *)pIrp->AssociatedIrp.SystemBuffer; if (uInputLength >= MIN_EDID_LENGTH && pHwDeviceExtension != nullptr && pHwDeviceExtension->pMonitor != nullptr) { pHwDeviceExtension->pMonitor->SetEdid(inputBuffer,uInputLength); } else { status = STATUS_INVALID_VARIANT; } break; } default: break; } IoCompleteRequest(pIrp,IO_NO_INCREMENT); IoUninitializeWorkItem(IoWorkItem); IoFreeWorkItem(IoWorkItem); KeReleaseSpinLock(&g_lock,OldIrql); }上述代码加了一个自旋锁,是由于多了一个worker线程,为防止在DoIoControl中先将IRP完成了,然后再在HandleIoControl中执行IoMarkIrpPending(pIrp);造成蓝屏,所以当前的逻辑确保在HandleIoControl中执行IoMarkIrpPending(pIrp);后再执行Worker队列中的任务。