自定义IRP的回收利用
			 2021-07-01
			  129
			 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函数。
			Windows驱动
			




