Во-первых, как асинхронный, так и синхронный ввод-вывод реализованы с помощью IRP. API пользовательского режима ReadFile вызывает внутренний NT API (системный вызов) NtReadFile, который в итоге отправляет IRP. Если драйвер возвращает STATUS_PENDING, NT API вернет тот же статус. Если приложение пользовательского режима выполняет синхронный вызов ReadFile, ReadFile будет ожидать в дескрипторе файла завершения ввода-вывода. Драйвер также может выполнять IRP синхронно (независимо от того, как вызывается API пользовательского режима). Я думаю, что это тот случай, который вас интересует.
IRP связаны с потоком, который их отправил. Поэтому, когда поток завершается (например, из-за уничтожения процесса), менеджер ввода-вывода пытается отменить все IRP, связанные с этим потоком. Поток не может завершиться, пока не завершены все IRP.
Когда вы закрываете дескриптор, менеджер ввода-вывода отправляет драйвер IRP_MJ_CLEANUP и IRP_MJ_CLOSE. Это драйвер , который отменяет ожидающие IRP (или просто завершает их как отмененные) в этом случае.
Возможность отмены IRP зависит от сотрудничества водителя. Драйвер должен явно отменить IRP, вызвав IoSetCancelRoutine .
Если драйвер просто блокируется без отмены IRP, IRP не будет отменен.