Удаление дескриптора из порта завершения ввода-вывода и другие вопросы о IOCP

на CreateIoCompletionPort функция позволяет создать новый порт завершения ввода-вывода и регистрации дескрипторов файлов к существующему порту завершения ввода-вывода.

тогда я могу использовать любую функцию, например recv на сокете или ReadFile в файле с OVERLAPPED структура для запуска асинхронной операции.

я должен проверить, вернулся ли вызов функции синхронно, хотя он был вызван с OVERLAPPED структура и в этом случае справиться с этим непосредственно. В другом случае, когда ERROR_IO_PENDING возвращается, я могу использовать GetQueuedCompletionStatus функция, которая будет уведомлена, когда операция завершится.

вопрос, который возникает, являются:

  • как я могу удалить дескриптор из порта завершения ввода-вывода? Например, когда я добавляю сокеты в IOCP, как я могу удалить закрытые? Должен ли я просто перерегистрировать другой сокет с тем же ключом завершения?

  • кроме того, есть ли способ чтобы вызовы всегда проходили через порт завершения ввода-вывода и не возвращались синхронно?

  • и, наконец, возможно ли, например,recv асинхронно, но send синхронно? Например, когда реализована простая служба echo: могу ли я ждать с асинхронным recv для новых данных, но send ответ синхронным образом, чтобы уменьшить сложность кода? В моем случае, я бы не recv второй раз в любом случае, прежде чем первый запрос был обработанный.

  • что произойдет, если асинхронный ReadFile был запрошен, но прежде чем он завершится, a WriteFile в тот же файл должен быть обработан. Будет ReadFile отменяется с сообщением об ошибке, и я должен перезапустить процесс чтения, как только запись завершена? Или мне нужно отменить ReadFile вручную, прежде чем писать? Этот вопрос возникает в сочетании с устройством связи; таким образом, запись и чтение не должны делать проблемы, если происходит одновременно.

4 ответов


как я могу удалить дескриптор из порта завершения ввода-вывода?

по моему опыту, вы не можете отделить дескриптор от порта завершения. Однако вы можете отключить уведомление порта завершения, установив бит низкого порядка вашего OVERLAPPED структуры hEvent поле: см. документацию к GetQueuedCompletionStatus.

например, когда я добавляю сокеты в IOCP, как я могу удалить закрытые? Я Должен просто перерегистрируйте другой сокет с тем же ключом завершения?

нет необходимости явно отделять дескриптор от порта завершения ввода-вывода; достаточно закрыть дескриптор. Вы можете связать несколько дескрипторов с одним и тем же ключом завершения; лучший способ выяснить, какой запрос связан с завершением ввода-вывода, - использовать OVERLAPPED структура. На самом деле, вы можете даже расширения OVERLAPPED для хранения дополнительных данных.

кроме того, есть ли способ заставить вызовы всегда проходить через порт завершения ввода-вывода и не возвращаться синхронно?

это поведение по умолчанию, даже если ReadFile/WriteFile возвращает TRUE. Вы должны явно вызвать SetFileCompletionNotificationModes указать Windows не поставит пакет завершения, когда TRUE и ERROR_SUCCESS возвращаются.

возможно ли, например,recv асинхронно, но send синхронно?

не с помощью recv и send; вам нужно использовать функции, которые принимают OVERLAPPED структуры, такие как WSARecv, WSASend или ReadFile и WriteFile. Было бы удобнее использовать последний, если ваш код предназначен для работы с несколькими типами дескрипторов ввода-вывода, такими как сокеты и именованные каналы. Эти функции обеспечивают синхронный режим, поэтому, если вы их используете, вы можете смешайте асинхронные и синхронные вызовы.

что произойдет, если был запрошен асинхронный файл чтения, но до его завершения должен быть обработан файл записи в тот же файл?

неявной отмены нет. Пока вы используете separate OVERLAPPED структуры для каждого чтения / записи на полнодуплексное устройство, я не вижу причин, почему вы не можете выполнять параллельные операции ввода-вывода.


как я уже указал здесь, общепринятое мнение, что невозможно удалить дескрипторы из портов завершения, неверно, вероятно, вызвано отсутствием какого-либо намека на то, как это сделать почти из всей документации, которую я мог найти. На самом деле, это довольно просто:

вызов NtSetInformationFile С FileReplaceCompletionInformationзначение перечислителя для FileInformationClass и указатель на FILE_COMPLETION_INFORMATION структура


во-первых, некоторые важные исправления.

в случае, если перекрывающаяся операция ввода-вывода завершается немедленно (ReadFile или аналогичная функция ввода-вывода возвращает успех) - завершение ввода-вывода -уже запланировано к IOCP.

кроме того, в соответствии с вашими вопросами, я думаю, вы путаете между дескрипторами файлов/сокетов и конкретными операциями ввода-вывода, выданными на них.

теперь, что касается ваших вопросов:

  1. АФАИК нет обычный способ удалить дескриптор файла/сокета из IOCP (обычно вам просто не нужно этого делать). Вы говорите об удалении закрытые ручки из IOCP, что абсолютно неверно. Вы не можете удалить закрытый дескриптор, потому что он больше не ссылается на допустимый объект ядра!

более правильным вопросом должно быть то, как файл/сокет должен быть правильно закрыт. Ответ: просто закройте ручку. Все выдающие I / O операции (выданные на этом дескрипторе) скоро вернутся с кодом ошибки (аборт). Затем, в вашей процедуре завершения (тот, который вызывает GetQueuedCompletionStatus в цикле) должен выполнить per-I / O необходимую очистку.

  1. как я уже сказал, Все завершение ввода-вывода поступает в IOCP как в синхронном, так и в асинхронном случаях. Единственная ситуация, когда он не поступает в IOCP, - это когда ввод-вывод завершается синхронно с . В любом случае, если вы хотите Единой обработка-в таком случае вы можете опубликовать искусственные данные завершения в IOCP (используйте PostQueuedCompletionStatus).

  2. вы должны использовать WSASend и WSARecv (не recv и send) для перекрытого ввода-вывода Тем не менее, даже сокет был открыт с флагом WSA_FLAG_OVERLAPPED - вы разрешено для вызова функций ввода/вывода без указания OVERLAPPED структура. В таком случае эти функции работают синхронно. Так что вы можете принять решение о синхронных / асинхронных режимах для каждый вызов функции.

  3. нет проблем смешивать перекрывающиеся запросы на чтение/запись. Единственный деликатный момент здесь-это то, что происходит, если вы пытаетесь прочитать данные из позиции файла, в которую вы сейчас пишете. Результат может зависеть от тонких вещей, таких как порядок завершения ввода/вывода аппаратным обеспечением, некоторые параметры синхронизации ПК и т. д. Такой ситуации следует избегать.


как я могу удалить дескриптор из порта завершения ввода-вывода? Например, когда я добавляю сокеты в IOCP, как я могу удалить закрытые? Должен ли я просто перерегистрировать другой сокет с тем же ключом завершения?

вы неправильно поняли. Вы устанавливаете порт завершения ввода - вывода для использования файловым объектом-когда файловый объект удаляется, вам не о чем беспокоиться. Причина, по которой вы путаетесь, заключается в том, как Win32 предоставляет базовое собственные функции API (CreateIoCompletionPort делает две очень разные вещи в одну функцию).

кроме того, есть ли способ сделать звонки Всегда переходите через порт завершения ввода-вывода и не возвращаются синхронно?

так было всегда. Только начиная с Windows Vista можно настроить способ обработки уведомлений о завершении.

что произойдет, если асинхронный ReadFile был запрошен, но до ее завершения, а WriteFile для такой же файл должен быть обработан. Будет ли ReadFile отменен с помощью сообщение об ошибке, и я должен перезагрузить процесс чтения, как только запись завершена?

операции ввода/вывода в Windows являются асинхронными по своей сути, и запросы всегда очереди. Вы можете не думать, что это так, потому что вы должны указать FILE_FLAG_OVERLAPPED на CreateFile чтобы включить асинхронный ввод-вывод. однако на собственном уровне синхронный ввод-вывод действительно является дополнением, удобством, где ядро отслеживает положение файла и ожидает завершения ввода-вывода перед возвращением.