我之前写过一篇文章: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队列中的任务。