Windows驱动
+ -

自定义IRP的回收利用

2021-07-01 87 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函数。

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

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

您的支持,是我们前进的动力!