windows驱动WorkItem

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



猜你喜欢

转载自blog.csdn.net/dailongjian2008/article/details/80645395