自定义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
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函数。

  • USB基础
  • USB摄像头UVC
  • USB人机交互HID
  • USB音频UAC
  • Windows基础
  • 磁盘与文件系统
  • Windows编程
  • Windows驱动
  • 开发模块
  • Windows运维
  • Linux相关
  • C语言学习
  • 高级语言
  • 前端开发
  • 服务器开发
  • 数据库
  • 字节流笔记
  • 字节流
  • 取消
    感谢您的支持,我会继续努力的!
    扫码支持
    扫码打赏,你说多少就多少

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

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