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, 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 }
分享到:
相关推荐
ReactOS项目致力于为大家开发一个免费而且完全兼容 Microsoft Windows XP 的操作系统。ReactOS 旨在通过使用类似构架和提供完整公共接口实现与 NT 以及 XP 操作系统二进制下的应用程序和驱动设备的完全兼容。 ...
本书通过分析ReactOS的源代码介绍了Windows内核各个方面的结构、功能、算法与具体实现。全书从“内存管理”、“进程”、“进程间通信”、“设备驱动”等多个方面进行分析介绍,所有的分析都有ReactOS的源代码(以及...
本书通过分析ReactOS的源代码介绍了Windows内核各个方面的结构、功能、算法与具体实现。全书从“内存管理”、“进程”、“进程间通信”、“设备驱动”等多个方面进行分析介绍,所有的分析都有ReactOS的源代码(以及...
JS操作系统 Web上的统一操作系统。 使用的技术: HTML CSS JavaScript React-JS 如何在本地运行: 克隆仓库git clone https://github.com/NJACKWinterOfCode/JS-OS.git 进入JS-OS光盘进入JS-OS cd JS-OS 安装...
漫谈兼容内核之一:ReactOS怎样实现系统调用 漫谈兼容内核之二:关于kernel-win32的对象管理 漫谈兼容内核之三:Kernel-win32的文件操作 漫谈兼容内核之四:Kernel-win32的进程管理 漫谈兼容内核之五:Kernel-win32...
在本文档中,我们以Windows NT和ReactOS提供的Fibers实现为参考,介绍了可加载内核模块(LKM)实现。 虽然用户空间实现通常因其开销少且易于调试而成为首选,但是内核空间实现允许更深入地了解内核子系统的工作方式...
01:ReactOS怎样实现系统调用.pdf 02:关于kernel -win32的对象管理.pdf 03:关于kernel-win32的文件操作.pdf 04:Kernel-win32的进程管理.pdf 05:Kernel-win32的系统调用机制.pdf 06:二进制映像的类型识别.pdf 07...
React式对象实现。 结帐 用法 var obj = new ReactiveObject ( { 'foo' : '1' } ) ; obj . defineProperty ( 'bar' , 2 ) ; obj . foo = '2' ; obj . undefineProperty ( 'foo' ) ; // Don't use 'delete obj.foo' ...
远线程注入 每个进程都有自己的虚拟地址空间,对32位进程来说,这个地址空间的大小为4GB。...如下摘自ReactOS 3.14的代码所示,CreateRemoteThread实际实现的功能就是调用NtCreateThread创建一个属于目标进程的线程。
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、...
漫谈兼容内核之一:ReactOS怎样实现系统调用 漫谈兼容内核之二:关于kernel-win32的对象管理 漫谈兼容内核之三:Kernel-win32的文件操作 漫谈兼容内核之四:Kernel-win32的进程管理 漫谈兼容内核之五:Kernel-win32...
艺术家,专辑和曲目都以文件和文件夹的形式呈现,并且可以在Winamp重新实现轻松地进行操作和播放。现场环境动机构建软件应该保持乐趣。 这个项目主要是一个沙箱,我可以尝试并提高对React和TypeScript的了解。 这也...
isIpad() 设备型号为 iPad Device.isIphone() 设备型号为 iPhone Properties Device.model 设备型号,如 iPhone 或 iPad Device.deviceName 设备名称,如 John Smith 的 iPhone Device.systemName设备操作系统名称...
内存转储是通过使用NTFS事务完成的,NTFS事务使我们可以将转储写入内存,并且MiniDumpWriteDump API已被ReactOS的MiniDumpWriteDump实现改编所取代。 然后,BOF使用base64对内存中的数据进行编码,将其分块,然后...
谈兼容内核之一:ReactOS怎样实现系统调用.pdf 漫谈兼容内核之二:关于kernel -win32的对象管理.pdf 漫谈兼容内核之三:关于kernel-win32的文件操作.pdf 漫谈兼容内核之四:Kernel-win32的进程管理.pdf 漫谈兼容内核...
在许多情况下,操作员习惯于处理遥测,实时图表,警报等,这可以帮助他们确定有问题的机器并做出React以解决任何潜在的问题。 但是,一个问题浮现在脑海:如果机器可以自动调整自身并为用户提供自我修复功能,那...
功能,主题,UI / API文本,错误消息,命令内置支持: Arista EOS 鸟思科IOS-XR 思科IOS / IOS-XE 思科NX-OS FRRouting 了华为瞻博JunOS 米克罗蒂克诺基亚SR OS TNSR 虚拟操作系统对任何其他配置支持(可选)通过SSH...
xe-crawlerxe-crawler 是遵循声明式、可监测理念的分布式爬虫,其计划提供 Node.js、Go、Python 多种实现,能够对于静态 Web 页面、动态 Web 页面、关系型数据库、操作系统等异构多源数据进行抓取。xe-crawler 希望...
实现分块功能可以采用手动循环的对每个块依次操作,也可以使用Matlab提供的分块处理功能blkproc。 选择两张大小相同的图像,分别进行DFT变换后,置换两幅图像的幅度和相位信息后再作反变换,观察并分析结果。 主要...