Удаление дескриптора из порта завершения ввода / вывода и другие вопросы о IOCP - PullRequest
6 голосов
/ 04 июля 2011

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

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

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

Возникают следующие вопросы:

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

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

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

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

Ответы [ 4 ]

10 голосов
/ 04 июля 2011

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

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

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

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

Кроме того, есть ли способ, чтобы вызовы ВСЕГДА проходили через порт завершения ввода-вывода ине возвращаться синхронно?

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

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

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

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

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

6 голосов
/ 16 февраля 2016

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

Вызов NtSetInformationFile со значением перечислителя FileReplaceCompletionInformation для FileInformationClass и указателем на структуру FILE_COMPLETION_INFORMATION дляFileInformation параметр.В этой структуре установите для члена Port значение NULL (или nullptr в C ++), чтобы отсоединить файл от порта, к которому он в данный момент подключен (я думаю, если он не подключен к какому-либо порту, ничего не произойдет) или задайте для Port допустимый HANDLE для другого порта завершения, чтобы вместо этого связать файл с этим.

3 голосов
/ 04 июля 2011

Сначала некоторые важные исправления.

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

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

Теперь, что касается вашеговопросы:

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

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

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

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

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

0 голосов
/ 05 июля 2011

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

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

Также есть способ совершать звонки ВСЕГДА переходите через порт завершения ввода / вывода и не возвращаться синхронно?

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

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

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

...