Согласно MSDN ReadFile () функция Win32 может некорректно сообщать о завершении операции чтения. Когда?

В описании MSDN указано ReadFile() функция:

Если hFile открывается с FILE_FLAG_OVERLAPPED, параметр lpOverlapped должен указывать на действительную и уникальную структуру OVERLAPPED, в противном случае функция может неверно сообщить, что операция чтения завершена.

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

ReadFile(handle, &buf, n, &n_read, NULL);

Это означает, что он передает NULL как параметр lpOverlapped. Согласно документации, этот вызов не должен работать правильно в некоторых случаях. Я потратил много времени, пытаясь воспроизвести проблему, но мне это не удалось! Я всегда получал все данные в нужном месте в нужное время. Однако я тестировал только именованные каналы.

Кто-нибудь знает, когда я могу ожидать, что ReadFile () некорректно вернется и сообщит об успешном завершении, даже если данные еще не находятся в буфере? Что должно произойти, чтобы воспроизвести проблему? Это происходит с файлами, каналами, сокетами, консолями или другими устройствами? Должен ли я использовать определенную версию ОС? Или конкретная версия чтения (например, зарегистрируйте дескриптор порта завершения ввода-вывода)? Или конкретная синхронизация чтения и записи процессов / потоков?

Или когда это не удастся? Меня устраивает :/

Пожалуйста помоги!

С уважением, Мартин


person Martin Dobšík    schedule 18.03.2010    source источник


Ответы (4)


Внутренне система поддерживает только асинхронный ввод-вывод. Для синхронного ввода-вывода система создает временную структуру OVERLAPPED с hEvent = NULL;, выдает запрос асинхронного ввода-вывода, передавая это временное время, а затем ожидает завершения, используя GetOverlappedResult (bWait = TRUE).

Напомним, что hEvent временной OVERLAPPED структуры - это NULL, и обратите внимание на раздел «Примечания» в заголовке GetOverlappedResult:

Если член hEvent структуры OVERLAPPED равен NULL, система использует состояние дескриптора hFile, чтобы сигнализировать о завершении операции.

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

Теперь рассмотрим сценарий, в котором асинхронный файл HANDLE имеет ожидающий запрос ввода-вывода в то время, когда вы отправляете запрос синхронного ввода-вывода. Система создает структуру OVERLAPPED и ожидает завершения в hFile HANDLE. Тем временем асинхронный ввод-вывод завершается, тем самым сигнализируя HANDLE, что приводит к преждевременному возврату синхронного ввода-вывода без фактического завершения.

Хуже того, к тому времени, когда асинхронный ввод-вывод, инициированный в ответ на запрос синхронного ввода-вывода, завершится, он обновит временную структуру OVERLAPPED, которая больше не существует. Результат - повреждение памяти.

Полную историю можно найти по адресу The Old New Thing.

person IInspectable    schedule 22.12.2012
comment
Спасибо, но толком не помогает. Я реализую ssh-клиент, и любой может запустить его с перенаправлением любых дескрипторов stdio. Мне нужно знать, что это за ручки, чтобы я мог выбрать правильный способ чтения с них. Более того, было бы здорово, если бы у меня был только один код для чтения со всех РУЧК, а не столько процедур чтения, сколько существует типов объектов, из которых нужно читать. Не говоря уже о том, что невозможно узнать, что за предмет за ручкой. Как прекрасен мир POSIX по сравнению с этим адом устаревших интерфейсов. - person Martin Dobšík; 10.05.2013
comment
@Martin Если ответ на ваш вопрос не помог, значит, вы задали не тот вопрос. Если вы хотите задать другой вопрос, сделайте это. - person IInspectable; 10.05.2013
comment
Привет, Тим, ты прав. Ваш ответ ответил на мой вопрос. Хотя это все еще оставляет меня в неуверенности. Может ли ReadFile () неверно сообщить о завершении также по другим причинам? Документация это позволяет. Если бы я мог быть уверен, что описанный вами случай единственный, то для моего приложения это почти не было бы проблемой. В очередной раз благодарим за помощь. - person Martin Dobšík; 13.05.2013
comment
@Martin Когда я читал документацию, это единственный режим ошибки, который явно прописан, то есть выполнение синхронного ввода-вывода для дескриптора, открытого с помощью FILE_FLAG_OVERLAPPED. Насколько мне известно, ReadFile() вернет код ошибки при синхронном использовании с файлом, открытым с помощью FILE_FLAG_OVERLAPPED. Однако трубы и почтовые ящики в этом отношении - странные животные. В основном это сводится к тому, что они не поддерживают концепцию позиции файла, в которой NtReadFile() не работает при использовании с файлами. - person IInspectable; 15.05.2013

Похоже, вы находитесь в ситуации, когда вы сознательно вызываете API в нарушение задокументированных передовых практик. В таких ситуациях все ставки отключены. Это может сработать, а может и нет. If может работать на этой ОС, но не на следующей итерации ОС или следующем пакете обновления той же ОС. Что происходит при портировании на Win64? Будет ли работать тогда?

Вызов GetLastError () (или просмотр @ ERR, hr в отладчике) дает какое-либо полезное значение в дополнение к коду ошибки?

Я рекомендую вам вызвать его с действующей структурой OVERLAPPED, заставить его работать и устранить все сомнения (и возможность случайного отказа). Почему в вашем программном обеспечении может быть код с ошибками (и ошибки очень трудно воспроизвести), когда вы можете легко исправить проблему, используя действующую структуру OVERLAPPED?

person Stephen Kellett    schedule 18.03.2010
comment
Именно об этом я думаю. Проблема в том, что это работает, даже если сознательно нарушает лучшие практики. Поэтому до сих пор этого никто не заметил. Мы уже исправили это, но некоторые из наших клиентов могут использовать старую версию программного обеспечения. Вот почему я ищу серьезность проблемы. Вы когда-нибудь видели это воспроизведенным? - person Martin Dobšík; 18.03.2010

Зачем задавать вопрос, а не исправлять код для вызова API, как это было задумано?

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

Настоящая проверка - это прочитать канал до того, как появятся данные для чтения.

Но на самом деле вам стоит просто исправить код. Если ваша архитектура не может обрабатывать асинхронный ввод-вывод, удалите FILE_FLAG_OVERLAPPED из создания именованного канала.

person Adrian McCarthy    schedule 18.03.2010
comment
Напомню, что, например, любое приложение, которое читает из stdin с использованием среды выполнения C, имеет эту возможную проблему, если кто-то решает перенаправить его дескрипторы stdio из каналов, созданных с флагом OVERLAPPED. Как я могу исправить приложение, если у меня нет абсолютно никакого контроля над запуском моих программ в сторонней среде? Тогда как минимум нужен механизм для определения наличия флага OVERLAPPED в дескрипторе. Но его не существует! Или нет? См. Другие мои вопросы на этом форуме (stackoverflow.com/users/296846/martin-dobsik). - person Martin Dobšík; 22.03.2010
comment
Если вы не можете узнать, был ли HANDLE создан с помощью FILE_FLAG_OVERLAPPED, возможно, вам следует всегда предоставлять структуру OVERLAPPED. Также обратите внимание, что ReadFileEx не имеет такого же ограничения, как ReadFile, в отношении параметра lpOverlapped. Возможно, вы сможете использовать это и GetLastError, чтобы убедиться, что ваш ввод-вывод завершен. - person Adrian McCarthy; 22.03.2010

Когда они говорят

Blockquote Если hFile открывается с помощью FILE_FLAG_OVERLAPPED, параметр lpOverlapped должен указывать на действительную и уникальную структуру OVERLAPPED, иначе функция может неверно сообщить, что операция чтения завершена.

они означают, что в коде ничего не мешает ему работать, но есть также путь через их код, который может привести к ошибочным результатам. Тот факт, что вы не можете воспроизвести проблему с вашим конкретным оборудованием, не означает, что проблемы нет.

Если вы действительно хотите воспроизвести эту проблему, оставьте код как есть и продолжайте жить своей жизнью. Примерно в то время, когда вы совсем забыли об этой проблеме, всплывет странное поведение, которое не будет иметь очевидного отношения к вызову ReadFile. Вы будете тратить дни на выдергивание волос, и проблема будет появляться и исчезать случайным образом. В конце концов, вы найдете это и будете наказывать себя за несоблюдение инструкций. Был там, сделал это, не весело!

Другой способ воссоздать проблему - запланировать важную демонстрацию для вашего клиента. Тогда он обязательно потерпит неудачу!

Если вы не хотите забрызгивать свой код структурами OVERLAPPED и всеми соответствующими проверками возвращаемого значения, ожиданиями, событиями и т. Д., Вы можете написать функцию-оболочку, которая принимает дескриптор для чтения и тайм-аут. Просто замените вызовы ReadFile этой удобной оберткой.

person Brett    schedule 02.11.2010
comment
Очень забавно, но это не помогает ему оценить срочность ошибки, которая существует во всех его развернутых установках. - person Ben Voigt; 02.11.2010
comment
Смешно это или нет, но это реальность. Я подозреваю, что никто не может сообщить ему подробности, которые он ищет, не зная об ограничении в ReadFile (), которое вызывает потенциальную ошибку. - person Brett; 02.11.2010