自定义IRP的回收利用

前面说到,我们有时需要自已生成新的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 
BuildNewIrp(
    PDEVICE_OBJECT DeviceObject
)
{
    PIRP Irp = NULL;
    PFDO_DEVICE_EXTENSION FDODeviceExtension;

    FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;

    Irp = GetRecyleIrp(DeviceObject);
    if (!Irp)
    {
        Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
        if (!Irp)
        {
            return NULL;
        }
    }
    else
    {
        IoReuseIrp(Irp, STATUS_SUCCESS);
    }
    return Irp;
}

注意

使用上述的方法,涉及到LIST,所以肯定的加锁,这是第一。
第二就是当驱动卸载时,要手动的释放空闲的IRP.

以上的思路来源于对Reactos HIDClass源代码走读时所得,具体可详见HID源码分析

取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

Powered by bytekits.com,汇天下文字,成非凡梦想!!!