原文链接:http://www.cnblogs.com/zhuyp1015/archive/2012/03/14/2396595.html

  IRP(I/O request package)是操作系统内核的一个数据结构。应用程序与驱动程序进行通信需要通过IRP包。当上层应用程序需要与驱动通信的时候,通过调用一定的 API函数,IO管理器针对不同的API产生不同的IRP,IRP被传递到驱动内部不同的分发函数进行处理。对于不会处理的IRP包需要提供一个默认的分 发函数来处理。

现在我们来看一下IRP的结构:

typedef struct _IRP {

PMDL  MdlAddress;

ULONG  Flags;

union {

struct _IRP  *MasterIrp;

PVOID  SystemBuffer;

} AssociatedIrp;

LIST_ENTRY  ThreadListEntry;  //用来将 IRP挂入某个线程的 IrpList队列

IO_STATUS_BLOCK  IoStatus;  //用来返回操作的完成状况

KPROCESSOR_MODE  RequestorMode;

BOOLEAN  PendingReturned;

CHAR  StackCount;

CHAR  CurrentLocation;

BOOLEAN  Cancel;

KIRQL  CancelIrql;

PDRIVER_CANCEL  CancelRoutine;

PVOID UserBuffer;

union {

struct {

union {

KDEVICE_QUEUE_ENTRY DeviceQueueEntry;

struct {

PVOID  DriverContext[4];

};

};

PETHREAD  Thread;

LIST_ENTRY  ListEntry;

} Overlay;

} Tail;

} IRP, *PIRP;

MSDN 说IRP是一个半透明结构,开发者只能访问其中透明的部分。

其实数据结构 IRP 只是"I/O 请求包"IRP的头部,在 IRP 数据结构的后面还有一个IO_STACK_LOCATION 数据结构的数组,数组的大小则取决于 IRP 数据结构中的StackCount,其数值来自堆叠中顶层设备对象的 StackSize 字段。这样,就在 IRP 中为目标设备对象堆叠中的每一层即每个模块都准备好了一个 IO_STACK_LOCATION 数据结构。而CurrentLocation,则是用于该数组的下标,说明目前是在堆叠中的哪一层,因而正在使用哪一个 IO_STACK_LOCATION 数据结构。

先来对IRP结构进行说明。

第一个参数 PMDL  MdlAddress:

MdlAddress域指向一个内存描述符表(MDL),描述了一个与该IO请求关联的用户模式缓冲区。如果顶级设备对象的Flags域为 DO_DIRECT_IO,则I/O管理器为 IRP_MJ_READ或 IRP_MJ_WRITE请求创建这个MDL。如果一个IRP_MJ_DEVICE_CONTROL请求的控制代码指定METHOD_IN_DIRECT 或METHOD_OUT_DIRECT操作方式,则I/O管理器为该请求使用的输出缓冲区创建一个MDL。

下一个参数:AssociatedIrp

我们WDM驱动会用到AssociatedIrp.SystemBuffer,这是一个指向系统空间的缓冲区。当使用直接IO的时候,这个缓冲区的 用途由与IRP相关的Majorfunction决定。对于IRP_MJ_READ和IRP_MJ_WRITE,则不会用到这个缓冲区。对于 IRP_MJ_DEVICE_CONTROL 或 IRP_MJ_INTERNAL_DEVICE_CONTROL这两类IRP,该缓冲区被作为DeviceIoControl函数的输入缓冲区。该缓冲区 的长度由IO_STACK_LOCATION结构(后面会讲到该结构)中的 Parameters.DeviceIoControl.InputBufferLength 成员来确定。

IoStatus(IO_STATUS_BLOCK)是一个仅包含两个域的结构,驱动程序在最终完成请求时设置这个结构。 IoStatus.Status 表示IRP完成状态,IoStatus.information的值与请求相关,如果是数据传输请求,则将该域设置为传输的字节数。

CurrentLocation(CHAR)和 Tail.Overlay.CurrentStackLocation(PIO_STACK_LOCATION)没有公开为驱动程序使用,但可以通过 IoGetCurrentIrpStackLocation函数获取这些信息。

说到IRP结构的CurrentLocation,我们可以来看一下IO_STACK_LOCATION结构了。

任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的 IO_STACK_LOCATION 结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序,堆栈单元中包含该IRP的类型代码和参数信息以及完成函数的地址。

说简单些就是在分层驱动中使用CurrentLocation来记录IRP到达了哪一层,在不同的层有对应的处理函数(通过IO_STACK_LOCATION关联),对IRP进行特定的处理。

IO_STACK_LOCATION结构为:

typedef struct _IO_STACK_LOCATION {

UCHAR  MajorFunction;

UCHAR  MinorFunction;

UCHAR  Flags;

UCHAR  Control;

Union

{

}Parameters;

PDEVICE_OBJECT  DeviceObject;

PFILE_OBJECT  FileObject;

PIO_COMPLETION_ROUTINE CompletionRoutine;

PVOID  context;

} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

MajorFunction指示驱动程序应该使用哪个函数来处理IO请求。

MinorFunction 进一步指出该IRP属于哪个主功能类

Flags 表明IO请求类型。

DeviceObject(PDEVICE_OBJECT)是与该堆栈单元对应的设备对象的地址。该域由IoCallDriver函数负责填写。

CompletionRoutine(PIO_COMPLETION_ROUTINE)是一个I/O完成例程的地址,该地址是由与这个堆栈单元对应 的驱动程序的更上一层驱动程序设置的。通过调用IoSetCompletionRoutine函数来设置。设备堆栈的最低一级驱动程序并不需要完成例程, 因为它们必须直接完成请求。然而,请求的发起者有时确实需要一个完成例程,但通常没有自己的堆栈单元。这就是为什么每一级驱动程序都使用下一级驱动程序的 堆栈单元保存自己完成例程指针的原因。

现在对IRP和IO_STACK_LOCATION都有了一个初步的认识。当驱动程序对IRP完成了操作(对各个域的读写)之后,需要调用IoCompleteRequest表明IRP处理已经结束,并将IRP交还给IO管理器。

VOID IoCompleteRequest(

__in  PIRP Irp,

__in  CCHAR PriorityBoost

);

第二个参数一般设置为IO_NO_INCREMENT。具体可参见MSDN。

对缺省IRP我们可以这样编写函数来处理:

NTSTATUS xxxDispatchRoutine(IN PDEVICE_OBJECT  do,IN PIRP Irp)

{

PAGED_CODE();

KdPrint(("Enter xxxDispatchRoutine\n"));

Irp->IoStatus.Status = STATUS_SUCCESS;

Irp->IoStatus.Information = 0; // no bytes xfered

IoCompleteRequest( Irp, IO_NO_INCREMENT );

KdPrint(("Leave xxxDispatchRoutine\n"));

return STATUS_SUCCESS;

}

WDM驱动是分层的,经常需要将IRP包在各层驱动中传递,负责IRP传递的函数有下面几个:IoCallDriver()   IoSkipCurrentIrpStackLocation()   IoCopyCurrentIrpStackLocationToNext()。

函数分别的定义为(注意函数的参数):

NTSTATUS IoCallDriver(

__in     PDEVICE_OBJECT DeviceObject,

__inout  PIRP Irp

);

通过该函数,将IRP送到指定设备(第一个参数)的驱动程序进行处理。

VOID IoSkipCurrentIrpStackLocation(

[in, out]  PIRP Irp

);

#define IoSkipCurrentIrpStackLocation( Irp ) { \

(Irp)->CurrentLocation++; \

(Irp)->Tail.Overlay.CurrentStackLocation++; }

该函数其实就是一个宏定义,设置IRP中IO_STACK_LOCATION的指针,上面两个函数一般在过滤驱动中配合使用:

IoSkipCurrentIrpStackLocation(Irp);//location+1
IoCallDriver(deviceExtension->nextLower, Irp);//location-1

执行完上面两步之后,location正好跟调用者一样,IO_STACK_LOCATION中的内容也不变。Filter
driver常用此种手段转发IRP:收到一个IRP,获取或者修改其数据,继续转发,因为location没变所以上层驱动设置的
CompleteRoutine依然会被filter之下的那个驱动调用到,Filter driver 就像透明的一样。

VOID IoCopyCurrentIrpStackLocationToNext(

__inout  PIRP Irp

);

#define IoCopyCurrentIrpStackLocationToNext( Irp ) { \ 
    PIO_STACK_LOCATION __irpSp; \ 
    PIO_STACK_LOCATION __nextIrpSp; \ 
    __irpSp = IoGetCurrentIrpStackLocation( (Irp) ); \ 
    __nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); \ 
    RtlCopyMemory( __nextIrpSp, __irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); \ 
    __nextIrpSp->Control = 0; }

可以看出该函数是一个宏定义,注意这里拷贝的是IRP stack,并不会影响下层的IRP
stack。该函数一般和IoSetCompletionRoutine连用,一般用来处理异步的IRP包。每次调用
IoCopyCurrentStackLocationToNext()函数,就将本层的IRP stack 放当下层的IRP
stack顶端,当IoCompleteRequest函数被调用也就是IRP包被处理完成之后,IRP stack
会一层层堆栈向上弹出,如果遇到IO_STACK_LOCATION的CompletionRoutine非空,则调用这个函数,另外传进这个完成例程的
是IO_STACK_LOCATION的子域Context。

VOID IoSetCompletionRoutine(

__in      PIRP Irp,

__in_opt  PIO_COMPLETION_ROUTINE CompletionRoutine,

__in_opt  PVOID Context,

__in      BOOLEAN InvokeOnSuccess,

__in      BOOLEAN InvokeOnError,

__in      BOOLEAN InvokeOnCancel

);

该函数设定一个CompletionRountine,当IRP处理完成逐层弹出到设定了CompletionRountine的堆栈的时候,则通过这个CompletionRountine再次进行处理。

最后再介绍一下获取IRP当前堆栈位置的函数:

IoGetCurrentIrpStackLocation(PIRP Irp);

这其实是一个宏定义:

#define IoGetCurrentIrpStackLocation \

( Irp ) ( (Irp)->Tail.Overlay.CurrentStackLocation )

还有一个可获得IRP下层堆栈:

IoGetNextIrpStackLocation(PIRP  Irp);

#define IoGetNextIrpStackLocation( Irp ) (\
(Irp)->Tail.Overlay.CurrentStackLocation - 1 )

make it simple, make it happen

从IRP说起(转)的更多相关文章

  1. IRP完成例程返回值理解

    第一,完成例程里面直接返回STATUS_SUCCESS,这时候IRP已经继续向上回卷,失去了对IRP的控制. 第二,完成例程里面返回STATUS_MORE_PROCESSING_REQUIRED,仍具 ...

  2. IRP IO_STACK_LOCATION 《寒江独钓》内核学习笔记(1)

    在学习内核过滤驱动的过程中,遇到了大量的涉及IRP操作的代码,这里有必要对IRP的数据结构和与之相关的API函数做一下笔记. 1. 相关阅读资料 <深入解析 windows 操作系统(第4版,中 ...

  3. Windows xp 重载内核(使用Irp进行文件操作)

    一.前言 最近在阅读A盾代码A盾电脑防护(原名 3600safe)anti-rootkit开放源代码,有兴趣的可以去看雪论坛下载,本文代码摘自其中的重载内核. 二.实现步骤 1.ZwQuerySyst ...

  4. 派遣例程与IRP结构

    提到派遣例程,必须理解IRP(I/O Request Package),即"输入/输出请求包"这个重要数据结构的概念.Ring3通过DeviceIoControl等函数向驱动发出I ...

  5. 使用IRP进行文件操作

    使用IRP进行文件操作 首先声明这个是菜鸟—我的学习日记,不是什么高深文章,高手们慎看. 一定要先感谢为技术的进步而付出辛勤汗水的人,感谢他们对技术的共享. 一个通用IRP访问文件的十六进制编辑器(开 ...

  6. IRP派遣操作

    IRPTrace工具跟踪IRP 派遣函数(Dispathc Funtion)是windows驱动中的重要概念.驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的.用户模式 ...

  7. IRP 与 派遣函数

    什么是派遣函数: 派遣函数是 WIndows 驱动程序中的重要概念.驱动程序的主要功能是负责处理I/O请求,其中大部分I/O请求是在派遣函数中处理的.也就是说,派遣函数是用来处理驱动程序提交过来的 I ...

  8. 《Windows驱动开发技术详解》之IRP的同步

    应用程序对设备的同步异步操作: 大部分IRP都是由应用程序的Win32 API函数发起的.这些Win32 API本身就支持同步和异步操作.例如,ReadFile.WriteFile和DeviceIoC ...

  9. IRP的同步

    应用层对设备的同步与异步操作 以WriteFile为例,一般的同步操作是调用WriteFile完成后,并不会返回,应用程序会在此处暂停,一直等到函数将数据写入文件中并正常返回,而异步操作则是调用Wri ...

随机推荐

  1. poj 3463 Sightseeing( 最短路与次短路)

    http://poj.org/problem?id=3463 Sightseeing Time Limit: 2000MS   Memory Limit: 65536K Total Submissio ...

  2. Xcode 向6.0以后版本添加iOS开发空白模板

    打开finder,找到应用程序,找到xcode 右键显示包内容.按照如下目录进行查找:Contents ▸ Developer ▸ Platforms ▸ iPhoneOS.platform ▸ De ...

  3. 1037: [ZJOI2008]生日聚会Party - BZOJ

    Description 今天是hidadz小朋友的生日,她邀请了许多朋友来参加她的生日party. hidadz带着朋友们来到花园中,打算坐成一排玩游戏.为了游戏不至于无聊,就座的方案应满足如下条件: ...

  4. 1194: [HNOI2006]潘多拉的盒子 - BZOJ

    Description  Input 第一行是一个正整数S,表示宝盒上咒语机的个数,(1≤S≤50).文件以下分为S块,每一块描述一个咒语机,按照咒语机0,咒语机1„„咒语机S-1的顺序描述.每一块的 ...

  5. JavaBean 和 Map 之间互相转换

    JavaBean 和 Map 之间互相转换 import java.beans.BeanInfo; import java.beans.IntrospectionException; import j ...

  6. Extjs4 treePanel异步加载菜单(后台从数据库读取)

    运行环境:springMVC+mybatis 一.建表 说明:0表示此节点为非叶子节点,即此节点还包括了子节点:1表示此节点为叶子节点,即此节点没有子节点.:关于图标iconCls是从Extjs的文件 ...

  7. select count(*)和select count(1)的区别 (转)

    A 一般情况下,Select Count (*)和Select Count(1)两着返回结果是一样的 假如表沒有主键(Primary key), 那么count(1)比count(*)快, 如果有主键 ...

  8. PAT-乙级-1004. 成绩排名 (20)

    1004. 成绩排名 (20) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 CHEN, Yue 读入n名学生的姓名.学号.成绩,分 ...

  9. C++11新特性:自动类型推断和类型获取

    声明:本文是在Alex Allain的文章http://www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-functi ...

  10. Android 获取JSP或ASP的sessionId(Cookie)

    如果使用的是httpClient: try { HttpResponse response = HttpUtil.httpClient.execute(new HttpGet(url)); Heade ...