详解IRP.docx

上传人:b****2 文档编号:2760869 上传时间:2023-05-04 格式:DOCX 页数:66 大小:36.96KB
下载 相关 举报
详解IRP.docx_第1页
第1页 / 共66页
详解IRP.docx_第2页
第2页 / 共66页
详解IRP.docx_第3页
第3页 / 共66页
详解IRP.docx_第4页
第4页 / 共66页
详解IRP.docx_第5页
第5页 / 共66页
详解IRP.docx_第6页
第6页 / 共66页
详解IRP.docx_第7页
第7页 / 共66页
详解IRP.docx_第8页
第8页 / 共66页
详解IRP.docx_第9页
第9页 / 共66页
详解IRP.docx_第10页
第10页 / 共66页
详解IRP.docx_第11页
第11页 / 共66页
详解IRP.docx_第12页
第12页 / 共66页
详解IRP.docx_第13页
第13页 / 共66页
详解IRP.docx_第14页
第14页 / 共66页
详解IRP.docx_第15页
第15页 / 共66页
详解IRP.docx_第16页
第16页 / 共66页
详解IRP.docx_第17页
第17页 / 共66页
详解IRP.docx_第18页
第18页 / 共66页
详解IRP.docx_第19页
第19页 / 共66页
详解IRP.docx_第20页
第20页 / 共66页
亲,该文档总共66页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

详解IRP.docx

《详解IRP.docx》由会员分享,可在线阅读,更多相关《详解IRP.docx(66页珍藏版)》请在冰点文库上搜索。

详解IRP.docx

详解IRP

操作

IRP:

所有驱动开发人员都要知道的知识

(Draft)

翻译:

Puffel

2007-8-3

概要

在微软的

Windows操作系统家族中,都通过发送

I/O请求包(IRP,I/oRequestPackets)来进行

和驱动程序的通讯。

用来封装

IRP的数据结构不仅仅用来描述一个

I/O操作的请求本身的内

容,还要用来维护这一请求在一系列驱动程序中传递的过程中的相关状态信息。

实现这一数

....

据结构其实是为了两重目的,也就是说

IRP可以被定义

[理解]为:

 

.

一个放置

I/O请求的容器

或者

.

一个与线程无关的调用栈

从以上这两个角度来考虑

IRP,将有助于驱动开发人员理解作为驱动,程序应该如何去正确

响应发来的

I/O请求。

本文中关于例程(

routines)和问题的讨论,请参考最新的微软

Windows驱动开发包(WDK,

MicrosoftWindowsDriverKit)。

第一重定义:

IRP是一个放置

I/O请求的容器

操作系统以

IRP的形式进行绝大多数发给驱动程序的

I/O操作请求。

因为下列特点,使得

IRP

非常适合于这一目的:

 

.

IRP设计来可以被异步操作

.

IRP可以被在完成前取消

.

IRP设计来适合于需要多驱动协同完成的输入输出操作

IRP数据结构封装了一个被请求的驱动所需要的,用来回复这一请求的

[全部]信息。

而请求

...

则可能来自于用户模式或者内核模式。

其实不论请求来自那里,对于被请求的驱动程序,所

需要的信息是一样的。

所有的

IRP都包含有下“表一”中的两个部分:

 

.

一个用来描述主要

I/O请求的头部

.

一组用来描述下级请求(有时就被称作子请求,

sub-requests)的参数

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

IRPHeader

Parametersforsub-request

Parametersforsub-request

.

.

.

Parametersforsub-request

表一:

IRP的结构

其中,头部的数据尺寸是固定,且对所有

IRP都相同的。

而下级请求数组的尺寸则依

赖于会有多少个驱动要来操作这一请求。

 

IRP头部的内容:

一个

IRP通常会被某个由一些驱动程序组成的栈来执行。

每个

IRP的头部信息,都各自包

含着每个要操作这个

IRP的驱动程序会要使用的数据。

当一个给定的驱动正在处理一个

IRP

时,这个驱动就被定义为此

IRP的当前拥有者

(thecurrentowner)。

每一个

IRP的头部,都包含有以下的指针

(Pointer):

 

.

指向针对这一

IRP的用以读取输入以及写回输出的缓冲区

.

指向当前拥有这一

IRP的驱动程序的内存区域

.

指向一个由当前拥有这一

IRP的驱动提供的例程。

这一例程将由系统在

IRP被取

消时[后]调用。

.

指向当前的子请求的参数

另外,除了这些指针,

IRP头部还包含有其它数据,用来描述请求的状态和其它内在信息

(微

软未开放的信息

)。

 

IRP参数组:

IRP头部之后是子请求数组。

一个

IRP可以有不只一个子请求,这是由于

IRP通常会是

被由多个[一些]驱动程序组成的栈来操作。

每一个

IRP都被分配了一个固定数量的,这样的

子请求,通常是对应于设备栈中的每一个驱动程序有一个。

这个编号恰好与相应栈最顶端设

备的

StackSize相匹配[一致],虽然在栈中间的驱动其实可以被分配的小一点。

还有,如果

一个驱动程序需要把请求发送到一个不同的设备栈处理,这个驱动程序就必须重新分配[创

建]一个新的

IRP。

每一个子请求都表示一个对应的

I/O栈的位置

(一个类型为

IO_STACK_LOCATION的结构),

并且特定的

IRP通常[典型的

]为每一个其被发送到的目的设备栈上的每个驱动仅维持

[包含

]

一个这样的栈位置信息。

IRP头部的一个域中还包含有标识当前正在被使用的

I/O栈位置

信息。

这个域的值被称作

IRP栈指针

(IRPstackpointer)或者当前栈位置

(currentstack

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

location)。

对于特定的

IO_STACK_LOCATION结构,会包含有以下内容:

 

l对于特定

IRP的主要以及次要的方法代码

l针对这些代码的参数

l一个指向相应驱动的设备对象(

deviceobject)的指针

l一个指向

IoCompletion例程的指针,如果驱动有过设定

l一个指向和这一请求相关联的文件对象的指针

l一些可变标志以及上下文

[关联]区域

特定的

IO_STACK_LOCATION结构并不包含有指针标识

[指向]输入和输出位置,这些(输

入、输出)指针本身就由

IRP自身所包含。

所有的子请求操作都使用同一缓冲区进行。

第二重定义:

IRP作为一个线程无关的调用栈

进行一个设备的

I/O操作通常[典型的

]需要调用这一设备相关的不止一个驱动。

每一个和这

一设备相关的驱动都会创建一个设备对象

(deviceobject),并且这些设备对象会垂直压入(排

列进)一个设备栈

(devicestack)中。

IRP会在设备栈中从上到下的一个个被传递过去(进去)。

对于栈中的每一个驱动,

IRP都会用(包含)一个指针标识一个栈位置。

由于驱动可以异步

的处理请求,因此

IRP就像是一个线程无关的调用栈一样,就像表二中所表示的:

 

ThreadStack

Parameter1forA

Parameter2forA

Returnaddresstoinitiator

Parameter1forB

Parameter2forB

ReturnaddresstoA

Parameter1forC

Parameter2forC

ReturnaddresstoB

Parameter1forAIoCompletionroutinefor

initiatorParameter2forA

Parameter1forBParameter2forB

Parameter1forCParameter2forC

IoCompletionroutineforA

IoCompletionroutineforB

IRPHeader

表二:

IRP作为线程无关的调用栈

在表二中,左侧,线程栈表示了驱动程序

A,B和

C的参数和返回值地址是如何在调用栈

(call

stack)中被组织的;而右侧,图表展示了这些参数和返回值地址是如何在一个

IRP中对应到

I/O栈位置以及

IoCompletion例程上的。

 

IRP操作所具有的异步特质对于操作系统和

Windows驱动模型

(WDM,WindowsDriver

Model)是非常关键的。

在一个同步的,单线程的

I/O结构设计中,发送了请求的应用程序以

及传递请求的驱动程序都要在更低层的组件完成请求任务之前处于等待中。

这样的设计对于

运用系统资源是很没有效率的,会降低整个系统的性能。

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

IRP的结构提供了一种可被继承的异步方式的设计,使得应用程序能够将一个或多个

I/O请

求排入队列,而不必等待。

I/O请求被处理的过程中,这个应用程序被解放出来可以进行

其他的计算,或者向队列里排入更多的

I/O请求。

由于所有被用来处理请求的信息都被以

IRP

的形式封装起来了,所以请求线程的调用栈可以从

I/O请求中分离出去。

IRP传递到下一级的驱动程序

IRP传递到下一级驱动程序

(又被称作为转发

IRP)是指

IRP等价于一个子例程调用。

当驱

动转发一个

IRP,它(驱动程序)必须向

IRP参数组增加下一个

I/O栈位置,告知这一

IRP

栈的指针,然后调用下一驱动的分发例程(dispatchroutine)。

基本说来,就是驱动向下调用

IRP栈(callingdowntheIRPstack)。

传递一个

IRP,驱动通常(典型的

)会采取以下几个步骤:

 

1.

建立下一个

I/O栈位置的参数。

驱动程序可以采取:

.

调用

IoGetNextIrpStackLocation例程来得到一个指针指向下一个

I/O栈位置,

然后将请求参数组复制到那个得到的位置。

.

调用

IoCopyCurrentIrpStackLocationToNext例程(如果驱动按第二步设置了

IoCompletion例程),或者

IoSkioCurrentIrpStackLocation例程(如果没有在第二

步设置

IoCompletion例程)来传递当前位置所使用的同样的参数组。

注意:

驱动程序不能够使用

RtlCopyMemory例程来复制当前的参数组。

这个

例程把指针复制到当前驱动的

IoCompletion例程,而且这样会导致

IoCompletion例程被调用不止一次(重入?

)。

 

2.

如果需要的话,调用

IoSetCompletionRoutine例程,为后期处理

(post-porcessing)设

置一个

IoCompletion例程。

如果驱动设置了

IpCompletion例程,那么他在上一步(第

一步)中必须使用

IoCopyCurrentIrpStackLocationToNext。

3.

通过调用

IoCallDriver例程将请求传递到下一个驱动。

这个例程会自动通告

IRP栈

指针,并且调用下一个驱动的分发例程。

在驱动程序将

IRP传递个下一个驱动之后,就不再拥有这个

IRP,并且不能试图再去访问这

个它。

否则会导致系统崩溃。

那个

IRP会被其它的驱动或者线程释放或完成。

如果驱动需

要访问一个已经在栈里传下去的

IRP,这个驱动必须实现

(设置)IoCompletion例程。

I/O

管理器(I/OManager)调用

IoCompletion例程时,这个驱动就能够在

IoCompletion例程执行期

间重新获得对这一

IRP的所有权。

如此,IoCompletion例程就能够访问

IRP中的域。

若是驱动的分发例程也还须在

IRP被后面的驱动处理完成之后再处理它,这个

IoCompletion

例程必须返回

STATUS_MORE_PROCESSING_REQUIRED,以将

IRP的所有权返回给分发

例程。

如此一来,

I/O管理器会停止

IRP的处理,将最终完成

IRP的任务留给分发例程。

发例程能够在之后调用

ICompleteRequest来完成这个

IRP,或者还能将这个

IRP标记为等候

进一步处理。

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

完成(齐整)一个

IRP

当输入、输出操作(

I/O)完成时,完成这个

I/O操作的驱动会调用

IoCompleteRequest例程。

这个例程将

IRP栈指针移到指向

IRP栈中的前一个(更上面)的位置,如表三所示。

 

IRP

stack

pointer

 

IRP

completion

Parameter1forACallbackforinitiatorParameter2forA

Parameter1forBParameter2forBIoCompletionroutineforA

Parameter1forCParameter2forCIoCompletionroutineforB

IRPHeader

CurrentI/Ostacklocation

IoStatus.Information

IoStatus.Status

表三:

IRP的完成过程及栈指针

表三表示出了在驱动

C调用

IoCompleteRequest后的当前栈位置。

左侧的实线箭头表示出栈

指针现在指向了驱动

B的参数组和回调函数。

点虚线箭头表示出前一个栈位置。

右侧的中

空箭头表示出

IoCompletion例程被调用的顺序。

注意:

为了解释上的方便,本文把

IRP中

I/O栈位置的顺序表示成“倒置”的,就是说,是

A到

C的反向顺序,而不是从

C到

A。

用这样的倒置图表是为了更直观用“向下”将调

用在栈中沿向下的方向进行表现出来。

如果一个驱动在设备栈中向下传递

IRP时设定了

IoCompletion例程,I/O管理器就会在

IRP

栈指针再次指向这一驱动的这个

I/O栈位置时调用此例程。

如此一来,

IoCompletion例程就

表现成为,当

IRP在设备栈中传递时,操作

IRP的那些驱动的返回地址。

一个

IoCompletion例程能够返回两个状态值中的任一个:

 

.

STATUS_CONTINUE_COMPLETION–继续向上完成特定

IRP。

I/O管理器提升

IRP栈指针,并且调用上一个驱动的

IoCompletion例程。

.

STATUS_MORE_PROCESSING_REQUIRED–中断向上完成特定

IRP的过程,并

且把

IRP栈指针留在当前位置。

返回这一状态的驱动通常会在过后调用

IoCompleteRequest例程来重新开始向上完成特定

IRP的过程。

当每一个驱动都完成了它对应的子请求,

I/O请求就完成了。

I/O管理器从

Irp->IoStatus.Status

域取回请求的状态信息,并且从

Irp->IoStatus.Information域取回传输的字节数。

同步(化)I/O响应

虽然

Windows操作系统是为异步

I/O设计的,大多数应用程序还是发送同步

I/O请求。

驱动

程序也能够即发送同步又发送异步请求,同时也能响应不论是同步还是异步请求。

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

要判断一个请求是异步完成的还是同步的,驱动需要检查由

IoCallDriver返回的状态,如下

面的例程序所示:

 

TKEVEN;ntve.

 

t,enev(&ntveeEizaltiKeIni,ntvenEioaticifotN);FALS.

 

);rp(IxtNeToonticaLocktapSIrntreuryCIoCo.

 

p,Ire(inutRoontilempCoIoSe.

netioupRIrchat.

t,enev.

E,RU.

E,RU.

ERU.

;.

 

sstatu=t,ecbjeOicev(DerivDrllCaIo);rp.

 

/

.

//Chkecrfosouonhrncsyorsouonhrncsyan.letiompc.

/

.

 

ifusatst(==)NGDIEN_PUSATST.

 

//eodCtoedlanhusnorochynasenspoes.

//edttmioorf.ow.

 

.

 

nretur;usats.

 

驱动程序初始化一个事件,设置

I/O栈位置,设置一个

IoCompletion例程,再调用

IoCallDriver

去转发

IRP。

IoCallDriver返回来的状态标示了后面(底下)的驱动是否在同步操作这个

IRP,还是异步的。

如果请求是被异步方式操作的,

IoCallDriver返回

STATUS_PENDING。

如果后面的驱动以同步方式响应,

IoCallDriver会返回一个由更后面(下面)一驱动返回的

完成状态信息。

如同例程序所示,驱动只是简单的返回同一个完成状态。

当一个

IRP被同步的完成,则驱动由它的分发例程返回这个

IRP的完成状态。

在它上面的

那些驱动可以通过下列两种方法得到这个状态:

 

l在分发例程中,由

IoCallDriver返回的值获得。

l在

IoCompletion例程中,由

IRP的

IoStatus.Status域获得。

I/O管理器调用某个驱动的

IoCompletion例程,这个驱动就拥有了相应(特定)的

IRP,

从而能够访问到

IoStatus.Status域。

如果驱动没有设置

IoCompletion例程,它就不会在调用

IoCallDriver后拥有相应的

IRP,因此决不能访问

IRP中的域。

表四展现了驱动程序或者应用程序能够获得

IRP状态的两种方法。

为了解释的方便,表中

将那些

IoCompletion例程以及他们所调用(被调用?

)的参数组放在了同一个

I/O栈位置,

而不是一个跟低的位置。

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

Status=IoCallDriver(?

;Status=Irp->IoStatus.Status;

STATUS_ERROR

 

STATUS_ERROR

STATUS_RETRY

 

Parameter1forAParameter2forA

Parameter1forBParameter2forB

Parameter1forCParameter2forC

IoCompletionroutinefor

initiator

IoCompletionroutineforA

IoCompletionroutineforB

STATUS_RETRY

STATUS_SUCCESS

STATUS_SUCCESS

 

表四:

IoCallDriver例程返回的状态,以及对

IoCompletion例程的可用性

在表四的左侧,

IoCallDriver例程返回了由后面(下面?

)一个驱动报告上来的完成状态。

在右侧,IoCompletion例程由

IRP的

IoStatus.Status域读出那个状态。

若是

IRP以同步方式

完成,每一个驱动的

IoCompletion例程都会在

IoCallDriver之前返回,从而使得

IoCompletion

例程比分发例程更早得到状态的值。

表四所示驱动

C返回

STATUS_SUCCESS,驱动

B返回

STATUS_RETRY,而驱动

A返回

STATUS_ERROR。

那个

IRP的最终状态只能够由初始的请求发出者得到,其他的驱动只能

够读到由后面一个驱动返回的状态。

异步(化)IO响应

驱动程序应该在当它无法以一个时间区间

(Timelymanner)以同步方式完成一个

IO请求时,

由分发例程返回

STATUS_PENDING状态。

理解何时该返回

STATUS_PENDING对很多驱动

开发者都是一个问题。

一个驱动程序,因该在以下的情况返回

STATUS_PENDING:

 

l当它的分发例程有可能会在一个

IRP被完成以前就返回

l它会在另外一个线程中完成特定的

IRP

l它的分发线程在返回之前无法判断某个

IRP是否完成

这个驱动程序必须在它释放对特定

IRP的控制,以及在它返回

STATUS_PENDING之前,调

IoMarkIrpPending宏。

IoMarkIrpPending会在当前的

IO栈位置的

Control域,置

SL_PENDING_RETURNED位(bit)。

每当一个

IO栈位置被完成,

IO管理器都会将此位的值

复制给特定

IRP头部的

Irp->PendingReturned域,如表五所示:

 

GeneratedbyFoxitPDFCreator.FoxitSoftware

Forevaluationonly.

IRP

stack

pointer

IRPHeader

PendingReturned

Parameter1forAParameter2forA

Parameter1forBParameter2forB

Parameter1forCParameter2forC

IoCompletionroutinefor

initiator

IoCompletionroutineforA

IoCompletionroutineforB

ControlforA

ControlforB

IoMarkIrpPending(Irp);

CurrentI/Ostacklocation

SL_PENDING_RETURNED

ControlforC

表五:

准备待处理(

Pending)位

在表五中,驱动

C对

IoMarkIrpPending宏的调用将驱动

C的

IO栈位置中的

Control域上的

SL_PENDING_RETURNED置位。

当驱动

C完成了对

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 党团工作 > 入党转正申请

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2