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_READ和IRP_MJ_WRITE操作,如果顶级设备指定DO_BUFFERED_IO标志,则I/O管理器就创建这个数据缓冲区。对于IRP_MJ_DEVICE_CONTROL操作,如果I/O控制功能代码指出需要缓冲区(见第九章),则I/O管理器就创建这个数据缓冲区。I/O管理器把用户模式程序发送给驱动程序的数据复制到这个缓冲区,这也是创建IRP过程的一部分。这些数据可以是与WriteFile调用有关的数据,或者是DeviceIoControl调用中所谓的输入数据。对于读请求,设备驱动程序把读出的数据填到这个缓冲区,然后I/O管理器再把缓冲区的内容复制到用户模式缓冲区。对于指定了METHOD_BUFFERED的I/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;
等于一个枚举常量UserMode或KernelMode,指定原始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,TAG_IRP); #066 } #067 else #068 {
非定额增加分配的方式。 #069 /* Allocate the IRP With no Quota charge */ #070 Irp = ExAllocatePoolWithTag(NonPagedPool,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,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} (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |