Original link: http://www.cnblogs.com/zhuyp1015/archive/2012/03/14/2396595.html
The IRP (I/O request package) is a data structure of the operating system kernel. The application communicates with the driver through the IRP package. When the upper-level application needs to communicate with the driver, by invoking certain API functions, the IO Manager produces different irp,irp for different APIs that are passed to the driver's internal different distribution functions for processing. For IRP packages that are not processed, you need to provide a default distribution function to handle.
Now let's look at the structure of the IRP:
typedef struct _IRP {
...
PMDL mdladdress;
ULONG Flags;
Union {
struct _IRP *masterirp;
...
PVOID SystemBuffer;
} associatedirp;
List_entry Threadlistentry; The irplist queue used to hook an IRP into a thread
Io_status_block IoStatus; Used to return the completion status of an operation
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 says that the IRP is a semi-transparent structure, and developers can only access portions of the transparency.
In fact, the data structure IRP is just the head of the "I/O request Package" IRP, and there is an array of io_stack_location data structures behind the IRP data structure, and the size of the array depends on the stackcount in the IRP data structure, the value from the top-level device object in the stack. The StackSize field. In this way, a io_stack_location data structure is prepared in the IRP for each layer in the target device object stack, i.e. each module. Currentlocation, however, is the subscript used for the array, indicating which layer is currently in the stack and which io_stack_location data structure is being used.
The IRP structure is described first.
The first parameter pmdl mdladdress:
The MdlAddress field points to a memory descriptor (MDL) that describes a user-mode buffer associated with the IO request. If the flags domain of the top-level device object is do_direct_io, the I/O Manager creates the MDL for Irp_mj_read or irp_mj_write requests. If the control code for a Irp_mj_device_control request specifies a method_in_direct or method_out_direct operation, the I/O Manager creates an MDL for the output buffer used by the request.
Next parameter: ASSOCIATEDIRP
Our WDM driver uses the AssociatedIrp.SystemBuffer, which is a buffer point to the system space. When using direct IO, the use of this buffer is determined by the majorfunction associated with the IRP. For Irp_mj_read and Irp_mj_write, this buffer is not used. For two types of IRP, Irp_mj_device_control or Irp_mj_internal_device_control, the buffer is used as the input buffer for the DeviceIoControl function. The length of the buffer is determined by the Parameters.DeviceIoControl.InputBufferLength member in the io_stack_location structure, which is described later in this structure.
IoStatus (Io_status_block) is a structure that contains only two domains, and the driver sets this structure when the request is finalized. IoStatus.Status represents the IRP completion state, the value of IoStatus.Information is related to the request, and if it is a data transfer request, the domain is set to the number of bytes transferred.
Currentlocation (CHAR) and Tail.Overlay.CurrentStackLocation (pio_stack_location) are not exposed for use by the driver, but can be The Iogetcurrentirpstacklocation function obtains this information.
When it comes to the currentlocation of the IRP structure, we can look at the io_stack_location structure.
When any kernel-mode program creates an IRP, it also creates an array of io_stack_location structures associated with it: each stack cell in the array corresponds to a driver that will process the IRP. The Stack cell contains the type code and parameter information for the IRP and the address of the completion function.
It is simpler to say that the currentlocation is used in the layered drive to record which layer the IRP arrives at, and the corresponding processing function (through Io_stack_location Association) in the different layers, to deal with the IRP in a specific way.
The io_stack_location structure is:
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 indicates which function the driver should use to process the IO request.
Minorfunction further indicates which main function class the IRP belongs to
Flags indicates the IO request type.
DeviceObject (Pdevice_object) is the address of the device object that corresponds to the stack cell. The field is filled in by the IoCallDriver function.
Completionroutine (Pio_completion_routine) is the address of an I/O completion routine that is set by a more up-level driver for the driver corresponding to this stack cell. Set by calling the Iosetcompletionroutine function. The lowest-level drivers for the device stack do not need to complete the routines, because they must complete the request directly. However, the initiator of the request does sometimes need a completion routine, but usually does not have its own stack unit. This is why each level of the driver uses the next-level driver's stack unit to save its own completion routine pointer.
Now there is a preliminary understanding of the IRP and the io_stack_location. After the driver has completed operations on the IRP (read and write to each domain), it is necessary to call IoCompleteRequest to indicate that the IRP processing has ended and to return the IRP to the IO Manager.
VOID IoCompleteRequest (
__in Pirp IRP,
__in CChar Priorityboost
);
The second parameter is generally set to io_no_increment. See MSDN for details.
For the default IRP we can write a function like this to handle:
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 drivers are layered and often need to pass the IRP packets in each layer drive, and the functions responsible for the IRP transfer are as follows: IoCallDriver () ioskipcurrentirpstacklocation () Iocopycurrentirpstacklocationtonext ().
The function is defined as (note the parameters of the function):
NTSTATUS IoCallDriver (
__in Pdevice_object DeviceObject,
__inout pirp IRP
);
Through this function, the IRP is sent to the driver for the specified device (the first parameter) for processing.
VOID Ioskipcurrentirpstacklocation (
[In, out] PIRP IRP
);
#define Ioskipcurrentirpstacklocation (IRP) {\
(IRP)->currentlocation++; \
(IRP)->tail.overlay.currentstacklocation++; }
This function is actually a macro definition, set the io_stack_location pointer in the IRP, the above two functions are generally used in the filter drive:
Ioskipcurrentirpstacklocation (IRP);//location+1
IoCallDriver (Deviceextension->nextlower, IRP);//location-1
After executing the above two steps, location is exactly the same as the caller, and the content in Io_stack_location is unchanged. Filter driver is used to forward an IRP by receiving an IRP, acquiring or modifying its data, and continuing to forward because the location is not changed, so the completeroutine of the upper driver setting is still called by the driver under the filter, filter Driver is as transparent as it is.
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; }
You can see that the function is a macro definition, note that the IRP stack is copied here, and does not affect the underlying IRP stack. This function is generally used in conjunction with Iosetcompletionroutine to handle asynchronous IRP packets. Each time the Iocopycurrentstacklocationtonext () function is called, the IRP stack of this layer is placed at the top of the IRP stack of the current layer, and when the IoCompleteRequest function is called that the IRP packet is processed, The IRP stack pops up on a layer stack, and if you encounter Io_stack_location's completionroutine non-null, then this function is called, and the io_stack_location subdomain context is passed into this completion routine.
VOID Iosetcompletionroutine (
__in Pirp IRP,
__in_opt Pio_completion_routine Completionroutine,
__in_opt PVOID Context,
__in BOOLEAN invokeonsuccess,
__in BOOLEAN Invokeonerror,
__in BOOLEAN Invokeoncancel
);
The function sets a completionrountine, which is processed again through this completionrountine when the IRP processing is complete and pops up to the stack that sets the completionrountine.
Let's take a look at the function that gets the current stack position of the IRP:
Iogetcurrentirpstacklocation (Pirp IRP);
This is actually a macro definition:
#define IOGETCURRENTIRPSTACKLOCATION \
(IRP) (IRP)->tail.overlay.currentstacklocation)
There is also an IRP downlevel stack that can be obtained:
Iogetnextirpstacklocation (Pirp IRP);
#define Iogetnextirpstacklocation (IRP) (\
(IRP)->tail.overlay.currentstacklocation-1)
Starting from the IRP (ext.)