The Big Picture: A Work Order in an Envelope

Think of a big office. A customer asks for something. The front desk writes the request on a form, puts it in an envelope and sends it to department after department. Each department reads it, does its part, maybe stamps it, and passes it on. At the end the envelope comes back with the result.

In Windows, that envelope is the IRP (I/O Request Packet). Almost everything a driver does is in response to an IRP. If you understand IRPs, you understand how requests flow through Windows drivers.

9.1 What an IRP Is

What it is: An IRP is a data structure in kernel memory that describes one I/O request: "read 4096 bytes from this file," "send this to the device," "the device was just removed." It is the standard envelope Windows uses to talk to every driver.

What is inside an IRP:

PartMeaning
Major function codeThe kind of request (see table below)
Minor function codeA sub-kind (used mostly by Plug and Play and power requests)
Buffer informationWhere the data to read or write is in memory, and how big
Status blockWhere the result goes: success or error, and how many bytes were done
Stack locationsOne small slot per driver layer in the stack (see 9.3)
Cancel / completion infoNeeded to cancel the request or run code when it finishes

The major function codes you should know. Each starts with IRP_MJ_:

CodeWhen it is used
IRP_MJ_CREATEA program opens a file or device (CreateFile)
IRP_MJ_CLOSEThe last handle is closed
IRP_MJ_READA program reads data (ReadFile)
IRP_MJ_WRITEA program writes data (WriteFile)
IRP_MJ_DEVICE_CONTROLA program sends a special command (DeviceIoControl)
IRP_MJ_PNPPlug and Play events (start, stop, remove)
IRP_MJ_POWERPower changes (sleep, wake)

Remember the dispatch table in the driver object (Section 4.3)? This is what it is for: one function per major code. When an IRP of type IRP_MJ_READ arrives, the driver runs the function listed for READ.

9.2 IRP Creation by the I/O Manager

What it is: The I/O Manager is a part of the kernel (Chapter 8) that sits between programs and drivers. When a program asks for I/O, the I/O Manager builds the IRP.

Step by step, for a ReadFile call:

 1. Program calls ReadFile(handle, buffer, 4096)
        │            (a system call: ring 3 ─► ring 0)
        ▼
 2. I/O Manager finds the DEVICE OBJECT behind the handle
        ▼
 3. It ALLOCATES an IRP, fills in:
      - major function = IRP_MJ_READ
      - length = 4096, buffer = the program's (checked) memory
      - one stack location per driver in the stack
        ▼
 4. It sends the IRP to the TOP driver of the stack

How is the buffer handled? A driver in ring 0 must be careful with memory that belongs to a program (a bad pointer would crash the PC). So devices choose one of a few buffering methods:

MethodIdea
Buffered I/OThe I/O Manager makes a safe copy in kernel memory. Simple, a little slower
Direct I/OThe program's buffer is locked in RAM and described to the driver, so the device can use DMA straight into it (Section 11). Best for big data
NeitherThe driver gets the raw program address and must check it itself. Risky and rarely used

Because every program must go through the I/O Manager, and the I/O Manager checks access rights when the handle is opened (IRP_MJ_CREATE), this is also where security is enforced (Section 2.3).

9.3 The IRP Travels Down the Device Stack

What it is: Remember the device stack from Section 4.5? The IRP enters at the top and moves down, one driver at a time. Each driver has its own I/O stack location inside the IRP: a small slot that holds that driver's view of the request.

For each layer, a driver can do one of three things:

ChoiceWhat it meansExample
Pass it down"Not my job, or I'm only watching." Copy/skip the stack location and send to the next driverA filter that only counts requests
Handle it and complete it"I can finish this myself." It sets the result and ends the requestA driver answering "yes, device is ready"
Queue it (mark pending)"I started the work. The device needs time. I'll finish later." It returns "pending"A disk read that takes a moment
   I/O Manager
       │  IRP
       ▼
 ┌──────────────────┐
 │ Upper filter     │  looks at it ──► passes down
 ├──────────────────┤
 │ Function driver  │  works out the exact device command ──► passes down
 ├──────────────────┤
 │ Bus driver       │  sends it on the bus to the hardware
 └────────┬─────────┘
          ▼
       HARDWARE   (works... later raises an interrupt)

For a keyboard read, the IRP stays pending (waiting) inside the keyboard driver for as long as you don't press a key. When you press a key, the driver fills in the data and completes the IRP, and the waiting program gets its answer. That is why a program can wait for input without wasting CPU.

9.4 IRP Completion Routines

What it is: When a driver finishes an IRP (success or error), it completes it. This sends the IRP back up the stack, in reverse order, to the I/O Manager. On the way up, any driver that registered a completion routine gets called.

  Down:   top driver ──► middle driver ──► bottom driver ──► hardware
  Up:     top driver ◄── middle driver ◄── bottom driver ◄── "done!"
                  ▲              ▲
          completion       completion
          routine runs     routine runs (if registered)

What a completion routine is good for:

UseExample
CleanupFree memory the driver reserved for this request
Post-processingA filter decrypts data that just came from the disk
RetryIf an error occurred, send it again
StatisticsCount how many bytes were read

When the IRP reaches the top, the I/O Manager copies the result (status and byte count) into the program's memory, wakes the waiting program (or signals its completion event) and frees the IRP.

Where the interrupt fits in. Real hardware takes time. When a disk finishes a read, it raises an interrupt (Chapter 4, Section 3). The driver's ISR and DPC (Section 10) run, and the DPC typically completes the IRP. So the full trip is: IRP down → hardware works → interrupt → DPC → IRP completes upward.

9.5 Synchronous vs Asynchronous I/O

What it is: Two ways for a program to wait for I/O.

SynchronousAsynchronous (overlapped)
The program...Waits at the call until it is finishedCalls, gets control back at once, and continues
How it learns the resultThe function returns when doneAn event, a callback or a completion port tells it later
SimplicitySimple to writeMore work to write
Good forSmall, quick tasksServers, games, big files; doing many things at once
The IRPSame kind of IRP; the I/O Manager just makes the thread waitSame IRP; the thread carries on

Analogy: Synchronous is standing at the counter until your coffee is ready. Asynchronous is taking a buzzer and sitting down; the buzzer rings when it is ready.

In Windows code, asynchronous I/O is chosen by opening the file with the FILE_FLAG_OVERLAPPED flag and passing an OVERLAPPED structure. Windows' high-speed servers use I/O completion ports for this.

Important: Whether the program waits or not is the program's choice. The driver just receives an IRP either way. If the driver can't finish right now, it returns pending, and the I/O Manager decides whether to make the calling thread wait.

Summary

TermPlain meaning
IRPThe standard "envelope" describing one I/O request
Major function codeThe request type: CREATE, READ, WRITE, DEVICE_CONTROL, PNP, POWER...
I/O ManagerKernel component that builds the IRP and sends it to the top driver
Stack locationOne slot per driver inside the IRP
Pass down / complete / pendingThe three things a driver can do with an IRP
Completion routineCode called as the finished IRP climbs back up
Synchronous / asynchronousProgram waits / program carries on and is told later