Performing an I/O operation typically requires more than one driver for a device. Each driver for a device creates a device object, and these device objects are organized hierarchically into a device stack. IRPs are passed down the device stack from one driver to the next. For each driver in the stack, the IRP contains a pointer to an I/O stack location. Because the drivers can handle the requests asynchronously, an IRP is similar to a thread-independent call stack, as Figure 2 shows.
Figure 2. IRP as Thread-independent Call Stack
On the left side of Figure 2, the thread stack shows how the parameters and return address for drivers A, B, and C might be organized into a call stack. On the right, the figure shows how these parameters and return addresses correspond to the I/O stack locations and IoCompletion routines in an IRP.
The asynchronous nature of IRP handling is critical to the operating system and the Windows Driver Model (WDM). In a synchronous, single-threaded I/O design, the application that issues a request and each driver through which the request passes must wait until all lower components have completed the request. Such a design uses system resources inefficiently, thus decreasing system performance.
The structure of the IRP provides for an inherently asynchronous design, enabling applications to queue one or more I/O requests without waiting. While the I/O request is in progress, the application thread is free to perform calculations or queue additional I/O requests. Because all the information required to process the request is encapsulated in the IRP, the requesting thread’s call stack can be decoupled from the I/O request.