reactos操作系统实现(88)

IRP I/O request packet 的缩写,即 I/O 请求包。驱动与驱动之间通过 IRP 进行通信。而使用驱动的应用层调用的 CreatFile,ReadFile,WriteFile,DeviceIoControl 等函数,说到底也是使用 IRP 和驱动进行通信。

一个 IRP 由两部分组成。首先是头部或者叫包的固定部分,是一个 IRP 结构。紧跟在这个头部之后的是 I/O栈位置,这是一个 IO_STACK_LOCATION 结构的数组,这个数组中元素的个数是根据情况而定的,由 IoAllocateIrp( IN CCHAR StackSize , IN BOOLEAN ChargeQuota ) 时的参数 StackSize 决定。而 StackSize 通常由 IRP 发往的目标 DEVICE_OBJECT StackSize 决定。而这个 StackSize 是由设备对象连入所在的设备栈时,根据在设备栈中位置决定的。我们先看看 IRP 结构和 IO_STACK_LOCATION 结构的定义。实现代码如下:

#001  typedef struct _IRP {

 

标志IRP类型。

#002       CSHORT  Type;

 

IRP的长度和IRP栈的长度。

#003       USHORT  Size;

 

指向内存描述符列表。

#004       struct _MDL  *MdlAddress;

 

IRP包特征标志,比如直接I/O,还是缓存I/O等等。

#005       ULONG  Flags;

 

保存驱动程序相关的数据结构。其中,与WDM驱动程序相关的指针是AssociatedIrp.SystemBuffer SystemBuffer指针指向一个数据缓冲区,该缓冲区位于内核模式的非分页内存中。对于IRP_MJ_READIRP_MJ_WRITE操作,如果顶级设备指定DO_BUFFERED_IO标志,则I/O管理器就创建这个数据缓冲区。对于IRP_MJ_DEVICE_CONTROL操作,如果I/O控制功能代码指出需要缓冲区(见第九章),则I/O管理器就创建这个数据缓冲区。I/O管理器把用户模式程序发送给驱动程序的数据复制到这个缓冲区,这也是创建IRP过程的一部分。这些数据可以是与WriteFile调用有关的数据,或者是DeviceIoControl调用中所谓的输入数据。对于读请求,设备驱动程序把读出的数据填到这个缓冲区,然后I/O管理器再把缓冲区的内容复制到用户模式缓冲区。对于指定了METHOD_BUFFEREDI/O控制操作,驱动程序把所谓的输出数据放到这个缓冲区,然后I/O管理器再把数据复制到用户模式的输出缓冲区。

#006       union {

#007              struct _IRP  *MasterIrp;

#008              volatile LONG  IrpCount;

#009              PVOID  SystemBuffer;

#010       } AssociatedIrp;

 

当前线程入口。

#011       LIST_ENTRY  ThreadListEntry;

 

一个仅包含两个域的结构,驱动程序在最终完成请求时设置这个结构。

#012       IO_STATUS_BLOCK  IoStatus;

 

等于一个枚举常量UserModeKernelMode,指定原始I/O请求的来源。驱动程序有时需要查看这个值来决定是否要信任某些参数。

#013       KPROCESSOR_MODE  RequestorMode;

 

IRP是否被阻塞。

#014       BOOLEAN  PendingReturned;

 

IRP栈的大小。

#015       CHAR  StackCount;

 

当前栈的位置。

#016       CHAR  CurrentLocation;

 

IRP是否被取消操作。

#017       BOOLEAN  Cancel;

 

IoAcquireCancelSpinLock函数调用,指明它是那一个IRQ级别。

#018       KIRQL  CancelIrql;

 

APC调用的环境索引。

#019       CCHAR  ApcEnvironment;

 

内存分配方式,比如定额地增加,还是固定大小等等。

#020       UCHAR  AllocationFlags;

 

保存用户I/O状态。

#021       PIO_STATUS_BLOCK  UserIosb;

 

保存用户的事件。

#022       PKEVENT  UserEvent;

 

APC、或分配的内存大小。

#023       union {

#024              struct {

#025                     PIO_APC_ROUTINE  UserApcRoutine;

#026                     PVOID  UserApcContext;

#027              } AsynchronousParameters;

#028              LARGE_INTEGER  AllocationSize;

#029       } Overlay;

 

驱动程序取消例程的地址

#030       volatile PDRIVER_CANCEL  CancelRoutine;

 

指向用户缓冲区。

#031       PVOID  UserBuffer;

 

 

#032       union {

 

设备队列入口,或者设备上下环境指针。

#033              struct {

#034                     _ANONYMOUS_UNION union {

#035                            KDEVICE_QUEUE_ENTRY  DeviceQueueEntry;

#036                            _ANONYMOUS_STRUCT struct {

#037                                   PVOID  DriverContext[4];

#038                            } DUMMYSTRUCTNAME;

#039                     } DUMMYUNIONNAME;

 

批向内核线程。

#040                     PETHREAD  Thread;

 

辅助缓冲区。

#041                     PCHAR  AuxiliaryBuffer;

 

I/O栈位置

#042                     _ANONYMOUS_STRUCT struct {

#043                            LIST_ENTRY  ListEntry;

#044                            _ANONYMOUS_UNION union {

#045                                   struct _IO_STACK_LOCATION  *CurrentStackLocation;

#046                                   ULONG  PacketType;

#047                            } DUMMYUNIONNAME;

#048                     } DUMMYSTRUCTNAME;

 

原来文件对象。

#049                     struct _FILE_OBJECT  *OriginalFileObject;

#050              } Overlay;

 

APC队列。

#051              KAPC  Apc;

 

I/O完成设置用户关键数据。

#052              PVOID  CompletionKey;

#053       } Tail;

#054  } IRP;

#055  typedef struct _IRP *PIRP;

 

上面学习了IRP的结构,知道了IRP保存的基本内容,也就是说知道了有什么相关东西,这就相当有了原材料,那么怎么样加工和处理这些原材料呢?那就得去分析IRP相关的操作函数,也就是IRP的相关算法。下面就从IRP分配开始,实现代码如下:

#001  PIRP

#002  NTAPI

#003  IoAllocateIrp(IN CCHAR StackSize,

#004                IN BOOLEAN ChargeQuota)

#005  {

#006      PIRP Irp = NULL;

 

计算IRP占用的大小,包括IRP的头部和IRP栈空间。

#007      USHORT Size = IoSizeOfIrp(StackSize);

#008      PKPRCB Prcb;

#009      UCHAR Flags = 0;

#010      PNPAGED_LOOKASIDE_LIST List = NULL;

#011      PP_NPAGED_LOOKASIDE_NUMBER ListType = LookasideSmallIrpList;

#012 

 

如果设置为定额分配方式,就添加这个标志位。

#013      /* Set Charge Quota Flag */

#014      if (ChargeQuota) Flags |= IRP_QUOTA_CHARGED;

#015 

#016      /* FIXME: Implement Lookaside Floats */

#017     

#018      /* Figure out which Lookaside List to use */

#019      if ((StackSize <= 8) && (ChargeQuota == FALSE))

#020      {

 

设置为固定分配大小空间。

#021          /* Set Fixed Size Flag */

#022          Flags = IRP_ALLOCATED_FIXED_SIZE;

#023 

 

需要使用一个大列表方式。

#024          /* See if we should use big list */

#025          if (StackSize != 1)

#026          {

#027              Size = IoSizeOfIrp(8);

#028              ListType = LookasideLargeIrpList;

#029          }

#030 

 

获取当前处理器控制块。

#031          /* Get the PRCB */

#032          Prcb = KeGetCurrentPrcb();

#033 

 

获取后备列表。

#034          /* Get the P List First */

#035          List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].P;

#036 

 

从后备列表里分配一个IRP包。

#037          /* Attempt allocation */

#038          List->L.TotalAllocates++;

#039          Irp = (PIRP)InterlockedPopEntrySList(&List->L.ListHead);

#040 

#041          /* Check if the P List failed */

#042          if (!Irp)

#043          {

#044              /* Let the balancer know */

#045              List->L.AllocateMisses++;

#046 

#047              /* Try the L List */

#048              List = (PNPAGED_LOOKASIDE_LIST)Prcb->PPLookasideList[ListType].L;

#049              List->L.TotalAllocates++;

#050              Irp = (PIRP)InterlockedPopEntrySList(&List->L.ListHead);

#051          }

#052      }

#053 

 

如果没有从后备列表里分配到IRP,就需要从内存里分配。

#054      /* Check if we have to use the pool */

#055      if (!Irp)

#056      {

 

从后备列表里分配失败。

#057          /* Did we try lookaside and fail? */

#058          if (Flags & IRP_ALLOCATED_FIXED_SIZE) List->L.AllocateMisses++;

#059 

 

定额增加分配的方式。

#060          /* Check if we should charge quota */

#061          if (ChargeQuota)

#062          {

#063              /* Irp = ExAllocatePoolWithQuotaTag(NonPagedPool, Size, TAG_IRP); */

#064              /* FIXME */

#065              Irp = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_IRP);

#066          }

#067          else

#068          {

 

非定额增加分配的方式。

#069              /* Allocate the IRP With no Quota charge */

#070              Irp = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_IRP);

#071          }

#072 

#073          /* Make sure it was sucessful */

#074          if (!Irp) return(NULL);

#075      }

#076      else

#077      {

#078          /* In this case there is no charge quota */

#079          Flags &= ~IRP_QUOTA_CHARGED;

#080      }

#081 

 

现在初始化IRP一些属性。

#082      /* Now Initialize it */

#083      IoInitializeIrp(Irp, Size, StackSize);

#084 

 

设置IRP分配的标志。

#085      /* Set the Allocation Flags */

#086      Irp->AllocationFlags = Flags;

#087 

 

返回分配成功的IRP包。

#088      /* Return it */

#089      IOTRACE(IO_IRP_DEBUG,

#090              "%s - Allocated IRP %p with allocation flags %lx/n",

#091              __FUNCTION__,

#092              Irp,

#093              Flags);

#094      return Irp;

#095  }

你可能感兴趣的:(react)