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

MSDN в своем описании функции ReadFile() указывает:

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

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

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

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

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

Или когда это не получится? У меня это работает: /

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

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

Ответы [ 4 ]

3 голосов
/ 22 декабря 2012

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

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

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

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

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

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

Полная история может быть найдена в Старая новая вещь .

0 голосов
/ 18 марта 2010

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

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

Настоящим тестом будет чтение канала перед тем, как будут прочитаны данные.

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

0 голосов
/ 02 ноября 2010

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

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

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

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

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

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

0 голосов
/ 18 марта 2010

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

Дает ли вызов GetLastError () (или глядя на @ ERR, hr в отладчике) какое-либо полезное значение в дополнение к коду ошибки?

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

...