I/O Manager (Kernel‑Mode)

Overview

The I/O Manager is a core component of the Windows kernel responsible for handling all I/O requests from user mode and managing communication between drivers and the operating system. It provides services such as device object creation, IRP life‑cycle management, and dispatch routine handling.

This documentation covers the primary concepts, key functions, structures, and best‑practice patterns for kernel‑mode drivers that interact with the I/O Manager.

Key Functions

DriverEntry

Entry point for a driver. Registers device objects and sets dispatch routines.

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ PUNICODE_STRING RegistryPath
);

IoCreateDevice

Creates a device object to represent a functional device.

NTSTATUS
IoCreateDevice(
    _In_ PDRIVER_OBJECT DriverObject,
    _In_ ULONG DeviceExtensionSize,
    _In_opt_ PUNICODE_STRING DeviceName,
    _In_ DEVICE_TYPE DeviceType,
    _In_ ULONG DeviceCharacteristics,
    _In_ BOOLEAN Exclusive,
    _Out_ PDEVICE_OBJECT *DeviceObject
);

IoDeleteDevice

Deletes a previously created device object.

VOID
IoDeleteDevice(
    _In_ PDEVICE_OBJECT DeviceObject
);

IoCreateSymbolicLink / IoDeleteSymbolicLink

Creates and removes a user‑mode symbolic link to the device.

NTSTATUS
IoCreateSymbolicLink(
    _In_ PUNICODE_STRING SymbolicLinkName,
    _In_ PUNICODE_STRING DeviceName
);
NTSTATUS
IoDeleteSymbolicLink(
    _In_ PUNICODE_STRING SymbolicLinkName
);

Important Structures

DEVICE_OBJECT

typedef struct _DEVICE_OBJECT {
    CSHORT                     Type;
    USHORT                     Size;
    LONG                       ReferenceCount;
    struct _DRIVER_OBJECT *DriverObject;
    struct _DEVICE_OBJECT *NextDevice;
    DEVICE_TYPE                DeviceType;
    ULONG                      Characteristics;
    PVOID                      DeviceExtension;
    // …
} DEVICE_OBJECT, *PDEVICE_OBJECT;

IO_STACK_LOCATION

typedef struct _IO_STACK_LOCATION {
    UCHAR  MajorFunction;
    UCHAR  MinorFunction;
    UCHAR  Flags;
    UCHAR  Control;
    union {
        PIO_SECURITY_CONTEXT SecurityContext;
        // …
    } Parameters;
    // …
} IO_STACK_LOCATION, *PIO_STACK_LOCATION;

IRP (I/O Request Packet)

typedef struct _IRP {
    CSHORT                     Type;
    USHORT                     Size;
    PMDL                       MdlAddress;
    ULONG                      Flags;
    union {
        struct _IRP *MasterIrp;
        PDEVICE_OBJECT DeviceObject;
    } AssociatedIrp;
    // …
    PIO_STACK_LOCATION CurrentIrpStackLocation;
    // …
} IRP, *PIRP;

IRP Model & Dispatch Routine

When an I/O request reaches a driver, the I/O Manager creates an IRP and calls the driver’s dispatch routine based on the request’s major function code.

NTSTATUS
DispatchRead(
    _In_ PDEVICE_OBJECT DeviceObject,
    _Inout_ PIRP Irp
){
    // Retrieve parameters from IRP stack
    PIO_STACK_LOCATION  irpSp = IoGetCurrentIrpStackLocation(Irp);
    // Process read request...
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = bytesRead;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

Sample Minimal Driver

This example demonstrates a simple driver that creates a device, handles create/close and read requests, and cleans up on unload.

#include <ntddk.h>

#define DRIVER_TAG 'tseD'

void DriverUnload(_In_ PDRIVER_OBJECT DriverObject);
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath);
NTSTATUS DispatchCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp);
NTSTATUS DispatchRead(_In_ PDEVICE_OBJECT DeviceObject, _Inout_ PIRP Irp);

NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    UNREFERENCED_PARAMETER(RegistryPath);
    PDEVICE_OBJECT deviceObject = NULL;
    UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\SampleIoMgr");
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\SampleIoMgr");

    NTSTATUS status = IoCreateDevice(
        DriverObject,
        0,
        &devName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &deviceObject);

    if (!NT_SUCCESS(status)) return status;

    status = IoCreateSymbolicLink(&symLink, &devName);
    if (!NT_SUCCESS(status)) {
        IoDeleteDevice(deviceObject);
        return status;
    }

    for (int i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
        DriverObject->MajorFunction[i] = DispatchCreateClose;

    DriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
    DriverObject->DriverUnload = DriverUnload;
    deviceObject->Flags |= DO_DIRECT_IO;
    return STATUS_SUCCESS;
}

void
DriverUnload(PDRIVER_OBJECT DriverObject)
{
    UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\DosDevices\\SampleIoMgr");
    IoDeleteSymbolicLink(&symLink);
    IoDeleteDevice(DriverObject->DeviceObject);
}

NTSTATUS
DispatchCreateClose(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS
DispatchRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
    UNREFERENCED_PARAMETER(DeviceObject);
    PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
    ULONG length = irpSp->Parameters.Read.Length;
    // Fill buffer with dummy data
    RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer, length);
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = length;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}