NTDDK定义的Stack Location结构体由以下几个字段组成:
MajorFunction:
该字段定义了一个函数功能集,内核模式驱动可以实现其中的每一个函数。每一个函数由一个函数代码对应。
当内核模式驱动接收到一个IRP时,驱动首先检查当前StackLocation中的MajorFunction字段,得出驱动将要执行的功能,可能的MajorFunction功能代码如下:
#define IRP_MJ_CREATE 0x00
#define IRP_MJ_CREATE_NAMED_PIPE 0x01
#define IRP_MJ_CLOSE 0x02
#define IRP_MJ_READ 0x03
#define IRP_MJ_WRITE 0x04
#define IRP_MJ_QUERY_INFORMATION 0x05
#define IRP_MJ_SET_INFORMATION 0x06
#define IRP_MJ_QUERY_EA 0x07
#define IRP_MJ_SET_EA 0x08
#define IRP_MJ_FLUSH_BUFFERS 0x09
#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
#define IRP_MJ_DIRECTORY_CONTROL 0x0c
#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
#define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f
#define IRP_MJ_SHUTDOWN 0x10
#define IRP_MJ_LOCK_CONTROL 0x11
#define IRP_MJ_CLEANUP 0x12
#define IRP_MJ_CREATE_MAILSLOT 0x13
#define IRP_MJ_QUERY_SECURITY 0x14
#define IRP_MJ_SET_SECURITY 0x15
#define IRP_MJ_POWER 0x16
#define IRP_MJ_SYSTEM_CONTROL 0x17
#define IRP_MJ_DEVICE_CHANGE 0x18
#define IRP_MJ_QUERY_QUOTA 0x19
#define IRP_MJ_SET_QUOTA 0x1a
#define IRP_MJ_PNP 0x1b
#define IRP_MJ_PNP_POWER IRP_MJ_PNP // Obsolete....
#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
MinorFunction
MinorFunction提供了MajorFunction更详细的信息
Flags
Flags提供了函数期望驱动执行的附加信息
Control
当内核驱动异步处理IRP时,驱动可以通过调用IoMarkIrpPending()将IRP标记为Pending状态,以排队IRP处理。IoMarkIrpPending实际上只是将当前Stack Location的Control字段设定为SL_PENDING_RETURNED。内核驱动可以检查该字段查询是否有该标记。
DeviceObject
当调用IoCallDriver()时,NT I/O管理器设定该字段,该字段的内容被设定为目标设备对象的指针。
Win2K源代码中可以看出上面的过程:
NTSTATUS
FASTCALL
IopfCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
/*++
Routine Description:
This routine is invoked to pass an I/O Request Packet (IRP) to another
driver at its dispatch routine.
Arguments:
DeviceObject - Pointer to device object to which the IRP should be passed.
Irp - Pointer to IRP for request.
Return Value:
Return status from driver's dispatch routine.
--*/
{
PIO_STACK_LOCATION irpSp;
PDRIVER_OBJECT driverObject;
NTSTATUS status;
//
// Ensure that this is really an I/O Request Packet.
//
ASSERT( Irp->Type == IO_TYPE_IRP );
//
// Update the IRP stack to point to the next location.
//
Irp->CurrentLocation--;
if (Irp->CurrentLocation <= 0)
{
KeBugCheckEx( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0, 0 );
}
irpSp = IoGetNextIrpStackLocation( Irp );
Irp->Tail.Overlay.CurrentStackLocation = irpSp;
//
// Save a pointer to the device object for this request so that it can
// be used later in completion.
//
irpSp->DeviceObject = DeviceObject;
//
// Invoke the driver at its dispatch routine entry point.
//
driverObject = DeviceObject->DriverObject;
PERFINFO_DRIVER_MAJORFUNCTION_CALL(Irp, irpSp, driverObject);
status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
Irp );
PERFINFO_DRIVER_MAJORFUNCTION_RETURN(Irp, irpSp, driverObject);
return status;
}
FileObject
I/O管理器将该字段设定为指向I/O操作的目标文件对象
CompletionRoutine
当调用IoSetCompletionRoutine()函数时,设定该字段。I/O管理器在IRP处理完成时,要进行一系列的事后处理,其中就包括检查CompletionRoutine。如果指定了CompletionRoutine,该函数就将在执行事后处理的线程中调用;一般情况下就是调用IoCompleteRequest()函数的线程上下文中。
CompletionRoutine采用堆栈顺序调用,即最后指定的CompletionRoutine最先调用,因此最高层驱动的完成函数最后被调用。如果某个驱动CompletionRoutine返回了STATUS_MORE_PROCESSING_REQUIRED,I/O管理器将立即停止IRP事后处理。返回STATUS_MORE_PROCESSING_REQUIRED的驱动有责任释放IRP占用的内存。
如果你开发一个高层的驱动程序,例如文件系统驱动或者过滤驱动,并且指定了一个Completion Routine。记得使用以下代码:
if(PtrIrp->PendingReturned)
{
IoMarkIrpPending(PtrIrp);
}
否则的话IRP处理将会出现错误。
Context
该字段为Completion Routine提供参数