windows内核开发学习笔记十六: IO_STACK_LOCATION
IO_STACK_LOCATION 结构定义了一个I/O stack location, 是每个IRP关联的 I/O stack的其中一个入口。 在IRP中的每个I/O stack location 都有一些通用的成员和一些与请求相关的成员。任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序,另外还有一个堆栈单元供IRP的创建者使用。堆栈单元中包含该IRP的类型代码和参数信息以及完成函数的地址。例如如下设备关系:
typedef struct _IO_STACK_LOCATION {
UCHAR MajorFunction;
UCHAR MinorFunction;
UCHAR Flags;
UCHAR Control;
union {
//
// IRP_MJ_CREATE 的参数
//
struct {
PIO_SECURITY_CONTEXT SecurityContext;
ULONG Options;
USHORT POINTER_ALIGNMENT FileAttributes;
USHORT ShareAccess;
ULONG POINTER_ALIGNMENT EaLength;
} Create;
//
// IRP_MJ_READ 的参数
//
struct {
ULONG Length;
ULONG POINTER_ALIGNMENT Key;
LARGE_INTEGER ByteOffset;
} Read;
//
// IRP_MJ_WRITE 的参数
//
struct {
ULONG Length;
ULONG POINTER_ALIGNMENT Key;
LARGE_INTEGER ByteOffset;
} Write;
//
// IRP_MJ_QUERY_INFORMATION 的参数
//
struct {
ULONG Length;
FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
} QueryFile;
//
// IRP_MJ_SET_INFORMATION 的参数
//
struct {
ULONG Length;
FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass;
PFILE_OBJECT FileObject;
union {
struct {
BOOLEAN ReplaceIfExists;
BOOLEAN AdvanceOnly;
};
ULONG ClusterCount;
HANDLE DeleteHandle;
};
} SetFile;
//
// IRP_MJ_QUERY_VOLUME_INFORMATION 的参数
//
struct {
ULONG Length;
FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass;
} QueryVolume;
//
// IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL 的参数
//
struct {
ULONG OutputBufferLength;
ULONG POINTER_ALIGNMENT InputBufferLength;
ULONG POINTER_ALIGNMENT IoControlCode;
PVOID Type3InputBuffer;
} DeviceIoControl;
//
// Nonsystem service parameters.
//
// IRP_MN_MOUNT_VOLUME 的参数
//
struct {
PVOID DoNotUse1;
PDEVICE_OBJECT DeviceObject;
} MountVolume;
//
// IRP_MN_VERIFY_VOLUME 的参数
//
struct {
PVOID DoNotUse1;
PDEVICE_OBJECT DeviceObject;
} VerifyVolume;
//
// Scsi using IRP_MJ_INTERNAL_DEVICE_CONTROL 的参数
//
struct {
struct _SCSI_REQUEST_BLOCK *Srb;
} Scsi;
//
// IRP_MN_QUERY_DEVICE_RELATIONS 的参数
//
struct {
DEVICE_RELATION_TYPE Type;
} QueryDeviceRelations;
//
// Parameters for IRP_MN_QUERY_INTERFACE
//
struct {
CONST GUID *InterfaceType;
USHORT Size;
USHORT Version;
PINTERFACE Interface;
PVOID InterfaceSpecificData;
} QueryInterface;
//
// Parameters for IRP_MN_QUERY_CAPABILITIES
//
struct {
PDEVICE_CAPABILITIES Capabilities;
} DeviceCapabilities;
//
// Parameters for IRP_MN_FILTER_RESOURCE_REQUIREMENTS
//
struct {
PIO_RESOURCE_REQUIREMENTS_LIST IoResourceRequirementList;
} FilterResourceRequirements;
//
// Parameters for IRP_MN_READ_CONFIG and IRP_MN_WRITE_CONFIG
//
struct {
ULONG WhichSpace;
PVOID Buffer;
ULONG Offset;
ULONG POINTER_ALIGNMENT Length;
} ReadWriteConfig;
//
// Parameters for IRP_MN_SET_LOCK
//
struct {
BOOLEAN Lock;
} SetLock;
//
// Parameters for IRP_MN_QUERY_ID
//
struct {
BUS_QUERY_ID_TYPE IdType;
} QueryId;
//
// Parameters for IRP_MN_QUERY_DEVICE_TEXT
//
struct {
DEVICE_TEXT_TYPE DeviceTextType;
LCID POINTER_ALIGNMENT LocaleId;
} QueryDeviceText;
//
// Parameters for IRP_MN_DEVICE_USAGE_NOTIFICATION
//
struct {
BOOLEAN InPath;
BOOLEAN Reserved[3];
DEVICE_USAGE_NOTIFICATION_TYPE POINTER_ALIGNMENT Type;
} UsageNotification;
//
// Parameters for IRP_MN_WAIT_WAKE
//
struct {
SYSTEM_POWER_STATE PowerState;
} WaitWake;
//
// Parameter for IRP_MN_POWER_SEQUENCE
//
struct {
PPOWER_SEQUENCE PowerSequence;
} PowerSequence;
//
// Parameters for IRP_MN_SET_POWER and IRP_MN_QUERY_POWER
//
#if (NTDDI_VERSION >= NTDDI_VISTA)
struct {
union {
ULONG SystemContext;
SYSTEM_POWER_STATE_CONTEXT SystemPowerStateContext;
};
POWER_STATE_TYPE POINTER_ALIGNMENT Type;
POWER_STATE POINTER_ALIGNMENT State;
POWER_ACTION POINTER_ALIGNMENT ShutdownType;
} Power;
#else
struct {
ULONG SystemContext;
POWER_STATE_TYPE POINTER_ALIGNMENT Type;
POWER_STATE POINTER_ALIGNMENT State;
POWER_ACTION POINTER_ALIGNMENT ShutdownType;
} Power;
#endif
//
// Parameters for IRP_MN_START_DEVICE
//
struct {
PCM_RESOURCE_LIST AllocatedResources;
PCM_RESOURCE_LIST AllocatedResourcesTranslated;
} StartDevice;
//
// Parameters for WMI Minor IRPs
//
struct {
ULONG_PTR ProviderId;
PVOID DataPath;
ULONG BufferSize;
PVOID Buffer;
} WMI;
//
// Others - driver-specific
//
struct {
PVOID Argument1;
PVOID Argument2;
PVOID Argument3;
PVOID Argument4;
} Others;
} Parameters;
PDEVICE_OBJECT DeviceObject;
PFILE_OBJECT FileObject;
.
.
.
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;
结构成员说明:
- MajorFunction:用于表示将要执行的I/O 类型的一个IRP major function code 。MajorFunction(UCHAR) : 是该IRP的主功能码。这个代码应该为类似IRP_MJ_READ一样的值,并与驱动程序对象中MajorFunction表的某个派遣函数指针相对应。如果该代码存在于某个特殊驱动程序的I/O堆栈单元中,它有可能一开始是,例如IRP_MJ_READ,而后被驱动程序转换成其它代码,并沿着驱动程序堆栈发送到低层驱动程序。
- MinorFunction:MajorFunction的一个功能子集代码,PnP管理器, 电源管理器, 文件系统驱动程序,SCSI一类的驱动程序在某些请求下设置这个成员。MinorFunction(UCHAR) : 是该IRP的副功能码。它进一步指出该IRP属于哪个主功能类。例如,IRP_MJ_PNP请求就有约一打的副功能码,如IRP_MN_START_DEVICE、IRP_MN_REMOVE_DEVICE,等等。
- Flags:一些与特定请求相关的值,这些值几乎只被文件系统独占使用。可移动媒介设备驱动程序为读请求检查这个标志是否被设置为SL_OVERRIDE_VERIFY_VOLUME,以确定是否在(即使)设备对象的 Flags 设置为DO_VERIFY_VOLUME的情况下继续执行读操作。(Removable-media device drivers check whether this member is set with SL_OVERRIDE_VERIFY_VOLUME for read requests to determine whether to continue the read operation even if the device object's Flags is set with DO_VERIFY_VOLUME.[求从句不要太长]) 在一个可移动介质的设备上使用的中间层驱动必须为所有进来的IRP_MJ_READ 请求拷贝这个成员到下一层的驱动程序的I/O stack location中。
- Control:驱动仅有只读这个成员的权限。驱动程序可以检查这个成员以确定它是否被设置为SL_PENDING_RETURNED。
- Parameters:一个依赖于MajorFunction and MinorFunction的主功能号,次要功能号的联合体参数(union),下列表格展示了Parameters 的union使用和IRP功能号的关系。Parameters(union) : 是几个子结构的联合,每个请求类型都有自己专用的参数,而每个子结构就是一种参数。这些子结构包括Create(IRP_MJ_CREATE请求)、Read(IRP_MJ_READ请求)、StartDevice(IRP_MJ_PNP的IRP_MN_START_DEVICE子类型),等等。
Member name | IRPs that use this member |
Create | IRP_MJ_CREATE |
Read | IRP_MJ_READ |
Write | IRP_MJ_WRITE |
QueryFile | IRP_MJ_QUERY_INFORMATION |
SetFile | IRP_MJ_SET_INFORMATION |
QueryVolume | IRP_MJ_QUERY_VOLUME_INFORMATION |
DeviceIoControl | IRP_MJ_DEVICE_CONTROL and IRP_MJ_INTERNAL_DEVICE_CONTROL |
MountVolume | IRP_MN_MOUNT_VOLUME |
VerifyVolume | IRP_MN_VERIFY_VOLUME |
Scsi | IRP_MJ_INTERNAL_DEVICE_CONTROL (SCSI) |
QueryDeviceRelations | IRP_MN_QUERY_DEVICE_RELATIONS |
QueryInterface | IRP_MN_QUERY_INTERFACE |
DeviceCapabilities | IRP_MN_QUERY_CAPABILITIES |
FilterResourceRequirements | IRP_MN_FILTER_RESOURCE_REQUIREMENTS |
ReadWriteConfig | IRP_MN_READ_CONFIG and IRP_MN_WRITE_CONFIG |
SetLock | IRP_MN_SET_LOCK |
QueryId | IRP_MN_QUERY_ID |
QueryDeviceText | IRP_MN_QUERY_DEVICE_TEXT |
UsageNotification | IRP_MN_DEVICE_USAGE_NOTIFICATION |
WaitWake | IRP_MN_WAIT_WAKE |
PowerSequence | IRP_MN_POWER_SEQUENCE |
Power | IRP_MN_SET_POWER and IRP_MN_QUERY_POWER |
StartDevice | IRP_MN_START_DEVICE |
WMI | WMI minor IRPs |
Others | Driver-specific IRPs |
- DeviceObject:指向一个驱动程序创建的设备对象DEVICE_OBJECT 结构,此结构代表了驱动为哪些目标物理,逻辑,虚拟设备处理IRP。是与该堆栈单元对应的设备对象的地址。该域由IoCallDriver函数负责填写。
- FileObject:一个FILE_OBJECT 结构的指针,此结构代表了DeviceObject 关联的文件对象(如果存在)。 是内核文件对象的地址,IRP的目标就是这个文件对象。驱动程序通常在处理清除请求(IRP_MJ_CLEANUP)时使用FileObject指针,以区分队列中与该文件对象无关的IRP。
- CompletionRoutine(PIO_COMPLETION_ROUTINE) : 是一个I/O完成例程的地址,该地址是由与这个堆栈单元对应的驱动程序的更上一层驱动程序设置的。你绝对不要直接设置这个域,应该调用IoSetCompletionRoutine函数,该函数知道如何参考下一层驱动程序的堆栈单元。设备堆栈的最低一级驱动程序并不需要完成例程,因为它们必须直接完成请求。然而,请求的发起者有时确实需要一个完成例程,但通常没有自己的堆栈单元。这就是为什么每一级驱动程序都使用下一级驱动程序的堆栈单元保存自己完成例程指针的原因。
- Context(PVOID) : 是一个任意的与上下文相关的值,将作为参数传递给完成例程。
补充说明以下几个事项:
- 对每个IRP而言, 一个driver stack中的每个驱动程序都有一个 IO_STACK_LOCATION ,每一个IRP的 I/O stack locations 附加在 IRP 结构之后。每一个高层级的驱动程序负责为每一个IRP的低一层驱动程序设置I/O stack location 。驱动程序对每一个IRP都必须使用IoGetCurrentIrpStackLocation 去获取一个它自己拥有的stack location 。更高层的驱动程序可以使用 IoGetNextIrpStackLocation 以获取底下一层的驱动程序的stack location。
- 高层级的驱动程序在传递IRP给低层的驱动程序时,必须在调用IoCallDriver 前设置stack location的内容 。如果驱动程序试图传递输入的IRP给下一层驱动程序(而不做任何修改),派发例程应当使用 IoSkipCurrentIrpStackLocation 或者是IoCopyCurrentIrpStackLocationToNext 为下一层驱动程序设置I/O stack location。
- 高级别的驱动程序调用 IoCallDriver 传递 DeviceObject 成员到下一层的驱动程序的目标设备对象中,在下一层的驱动程序的I/O stack location中。当IRP完成调用IoCompletionroutine 时,IO管理器将传递每个高层级的驱动程序的IoCompletion 例程到它拥有的设备对象中。
- 如果一个高层级的驱动程序申请创建了IRP以创建它自己的请求,如果没有为自己申请一个stack location 也没有在自己新申请的IRP拥有的stack location中安装 DeviceObject指针,他的IoCompletion 当被设置一个NULL DeviceObject 指针。
- 在某些情况下,一个在大容量储存设备上应用的高级别驱动程序,有责任为底层设备驱动分割大的传输请求。例如,SCSI类型的驱动程序必须检查 Parameters.Read.Length 和Parameters.Write.Length, 以确定请求的传输是否超过了了底层HBA的传输能力。如果是这样的话需要分割初始请求的 Length 为一系列一段段的传输请求以满足最初IRP的请求。