自定义IRP的回收利用
2021-07-01
98
0
前面说到,我们有时需要自已生成新的IPR并派发到下层驱动,所以这里的IRP派发一般的情况下是频繁的,所以上节中会出现频敏的IoAllocateIrp和IoFreeIrp。这样会造成性能上的损失,那有没有更好的处理方式了。
答案是肯定的:
在说答案之前,我们先介绍一个函数:IoReuseIrp,其REACCTOS的源码如下:
VOID NTAPI IoReuseIrp ( IN OUT PIRP Irp,
IN NTSTATUS Status
)
{
UCHAR AllocationFlags;
IOTRACE(IO_IRP_DEBUG,
"%s - Reusing IRP %p\n",
__FUNCTION__,
Irp);
/* Make sure it's OK to reuse it */
ASSERT(!Irp->CancelRoutine);
ASSERT(IsListEmpty(&Irp->ThreadListEntry));
/* Get the old flags */
AllocationFlags = Irp->AllocationFlags;
/* Reinitialize the IRP */
IoInitializeIrp(Irp, Irp->Size, Irp->StackCount);
/* Duplicate the data */
Irp->IoStatus.Status = Status;
Irp->AllocationFlags = AllocationFlags;
}
可以看到其调用了关键的函数 IoInitializeIrp,进行IRP的初始化。
VOID NTAPI IoInitializeIrp ( IN PIRP Irp,
IN USHORT PacketSize,
IN CCHAR StackSize
{
/* Clear it */
IOTRACE(IO_IRP_DEBUG,
"%s - Initializing IRP %p\n",
__FUNCTION__,
Irp);
RtlZeroMemory(Irp, PacketSize);
/* Set the Header and other data */
Irp->Type = IO_TYPE_IRP;
Irp->Size = PacketSize;
Irp->StackCount = StackSize;
Irp->CurrentLocation = StackSize + 1;
Irp->ApcEnvironment = KeGetCurrentThread()->ApcStateIndex;
Irp->Tail.Overlay.CurrentStackLocation = (PIO_STACK_LOCATION)(Irp + 1) + StackSize;
/* Initialize the Thread List */
InitializeListHead(&Irp->ThreadListEntry);
}
通过以上代码可以看到,利用原来的IRP内存空间,不过对IRP的各成员进行重新的初始化。
所以我们在上节的NewIrpCompleteRoutine中,将IRP挂入一个空闲IRP队列中,而不再进行释放。
这样当需要创建新的IRP进行下发时,可以先判断空闲队列是否为空,如果有空闲的,就取一个然后重新初始化,而如果没有的话,就老实的创建一个,再使用。
PIRP
GetIrp(
IN PFUNCTION_DEVICE_EXTENSION DeviceExtension)
{
KIRQL OldLevel;
PIRP Irp = NULL;
PLIST_ENTRY ListEntry;
KeAcquireSpinLock(&DeviceExtension->IrpCompletedListLock, &OldLevel);
if (!IsListEmpty(&DeviceExtension->IrpCompletedListHead))
{
ListEntry = RemoveHeadList(&DeviceExtension->IrpCompletedListHead);
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
}
KeReleaseSpinLock(&DeviceExtension->IrpCompletedListLock, OldLevel);
return Irp;
}
PIRP
BuildIrp(
IN PFUNCTION_DEVICE_EXTENSION DeviceExtension)
{
PIRP Irp;
Irp = GetIrp(DeviceExtension);
if (!Irp)
{
Irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize + 1, FALSE);
if (!Irp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
}
else
{
IoReuseIrp(Irp, STATUS_SUCCESS);
}
return Irp;
}
PIRP Irp;
Irp = BuildIrp(DeviceExtension);// IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize + 1, FALSE);
if (!Irp)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
注意
使用上述的方法,涉及到LIST,所以肯定的加锁,这是第一。
第二就是当驱动卸载时,要手动的释放空闲的IRP.
以上的思路来源于对Reactos HIDClass源代码走读时所得,具体可详见HID源码分析
hidclass.c HidClass_BuildIrp函数。