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 0x 0a
#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
#define IRP_MJ_DIRECTORY_CONTROL 0x 0c
#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
#define IRP_MJ_DEVICE_CONTROL 0x0e
#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x 0f
#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 0x 1a
#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
)
{
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提供參數