IO Subsystem
The Windows I/O Subsystem provides the core infrastructure that enables applications and drivers to communicate with hardware and the file system. It abstracts device interactions, manages I/O request packets (IRPs), and ensures synchronization, security, and performance across the system.
Overview
The I/O Subsystem consists of several components:
- I/O Manager – central dispatcher that creates, queues, and completes IRPs.
- Device Objects – represent physical or virtual devices.
- File Objects – represent open handles to devices or files.
- Driver Stack – chain of drivers that process a request.
Architecture
A typical I/O request flow follows these stages:
Application → Win32 API → I/O Manager → Driver Stack → Device → Completion → Callback
The diagram below illustrates the logical layers:
I/O Manager
The I/O Manager is responsible for:
- Creating and initializing
DEVICE_OBJECTstructures. - Managing reference counts and object lifetimes.
- Dispatching IRPs based on
IRP_MJ_*major function codes.
Typical entry points in a driver:
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
NTSTATUS MyAddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT PhysicalDeviceObject);
NTSTATUS MyDispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS MyDispatchWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp);
NTSTATUS MyUnload(PDRIVER_OBJECT DriverObject);
File System Drivers
File system drivers implement the IRP_MJ_CREATE, IRP_MJ_READ, IRP_MJ_WRITE, and other I/O operations for persistent storage. Examples include:
- NTFS –
ntfs.sys - FAT –
fat.sys - ReFS –
refsrfs.sys
Device Drivers
Device drivers handle communication with hardware. The driver model includes:
- Physical Device Objects (PDO) – represent actual hardware.
- Functional Device Objects (FDO) – expose functionality to the I/O Manager.
- Filter Drivers – sit above or below other drivers to modify behavior.
I/O Request Packet (IRP)
An IRP encapsulates all information needed for an I/O operation. Key fields include:
typedef struct _IRP {
CSHORT Type;
USHORT Size;
PMDL MdlAddress;
PVOID UserIosb;
PIO_STATUS_BLOCK IoStatus;
PCHAR StackLocation[1];
// ...
} IRP, *PIRP;
Drivers retrieve their specific stack location via IoGetCurrentIrpStackLocation.
Sample Code
Below is a minimal skeleton for a driver that handles read and write requests.
#include <ntddk.h>
NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
DriverObject->DriverUnload = DriverUnload;
DriverObject->MajorFunction[IRP_MJ_READ] = DriverRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = DriverWrite;
return STATUS_SUCCESS;
}
VOID
DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
DbgPrint("MyDriver unloaded\n");
}
NTSTATUS
DriverRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
// Complete with dummy data
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS
DriverWrite(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
UNREFERENCED_PARAMETER(DeviceObject);
// Accept data and complete
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = Irp->RequestorMode == KernelMode ?
Irp->IoStatus.Information : 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
For a full walkthrough, see the Sample Code section.