Асинхронный ввод/вывод (wasm.ru)


Предисловие

Хорошо, вы должны знать, что существует два типа операций ввода/вывода — синхронный и асинхронный типы. Используя функции асинхронного ввода/вывода вы будете ждать, пока операция ввода/вывода не будет закончена. Функции асинхронного ввода/вывода позволяют вам посылать запросы на выполнение операции ввода/вывода системе и немедленно продолжить выполнение кода. Когда операция асинхронного ввода/вывода будет закончена, система пошлет соответствующее уведомление.

Запрос на операцию асинхронного ввода/вывода

Первое, что вы должны сделать при работе с любым видом устройств — это открыть его с помощью CreateFile, задав флаг FILE_FLAG_OVERLAPPEd. Сделав это, мы можем использовать ReadFile и WriteFile.

  push указатель на структуру OVERLAPPED
  push указатель на количество прочтенных байтов
  push количество байтов, которое нужно прочитать
  push указатель на буфер, куда будет производиться чтение
  push хэндл устройства, из которого будет производиться чтение
  call ReadFile

  push указатель на структуру OVERLAPPED
  push указатель на количество записанных байтов
  push количество байтов, которое нужно записать
  push указатель на буфер, из которого будет браться информация для записи
  push хэндл устройства, в которое будет производиться запись
  call WriteFile

Структура OVERLAPPED — эта структура используется только для асинхронного ввода/вывода.

 overlapped struc
         _internal           dd ?
         _internalHigh       dd ?
         _offset             dd ?
         _offsetHigh         dd ?
         _eventHandle        dd ?
 overlapped ends
 _ol     overlapped <>

Прежде чем вызывать некоторые функции API ввода/вывода, некоторые поля данной структуры должны быть инициализированы.

указатель на количество записанных байтов — содержит количество прочтенных или записанных байтов.

Я думаю, что назначение других параметров достаточно ясно.

Получение результатов асинхронной операции

После запроса на выполенение операции мы делаем все, что хотим, но по прошествии некоторого времени нам, вероятно, понадобятся результаты операции ввода/вывода. Для этого есть четыре пути:

  • сигнализация объектов устройств ядра
  • прерываемый ввод/вывод
  • событийная сигнализация

Я сказал, что есть четыре пути, а упомянул только три… :))) Хорошо, последний называется ‘completion ports’. Он, как правило, используется серверами, поэтому я не буду тратить на него время… говоря словами J.R.Cimrman’а:

«Вы можете говорить об этом, вы можете не соглашаться, но это все, что вы можете сделать…»

А теперь — в путь!

Сигнализация объектов устройств ядра

Каждый хэндл устройства система считает синхронизационным объектом. Вызов функции ReadFile или WriteFile устанавливает хэндл устройства в несигнализирующее значение. После того, как асинронная операция ввода/вывода выполнена, хэндл устройства переход в значение сигнализирование. Поэтому мы можем подождать конца асинхронной операции ввода/вывода с помощью WaitForSingleObject или WaitForMultipleObject.

 push время ожидания
 push хэндл, которого ждем
 call WaitForSingleObject

время ожидания — период времени, в течении которого функция ждет сигнализации

 push время ожидания
 push булевое значение
 push указатель на поле хэндлов
 push количество проверяемых объектов - максимально 64
 call WaitForMultipleObjects

булевое значение — установка в TRUE(1) заставит функцию ждать все хэндлы, в противном случае функция возвратится после сигнализации одного хэндла

возвраты этих функций:

 WAIT_OBJECT_0  = 0    - то, чего мы ждали - объект был просигнализирован
                       - при использовании API XMultipleX при ожидании
                          одного объекта, мы получаем индекс этого хэндла
 WAIT_TIMEOUT   = 0102h - время ожидания истекло, объект не был
                          просигнализирован
 WAIT_FAILED    = -1    - произошла ошибка, используйте GetLastError для
                          получения расширенной информации

После вызова одной из этих функций мы значем, что операция ввода/вывода была завершена, однако мы не знаем ее результат. Чтобы узнать его, существует функция GetOverlappedResult.

 push булевое значение - ожидание
 push указатель на двойное слово - получаем байты перемещения
 push указатель на структуру OVERLAPPED ввода/вывода, который мы проверяем
 push handle
 call GetOverlappedResult

булевое значение — ожидание — если TRUE(1), тогда функция будет ждать, пока не будет выполнена операция асинхронного ввода/вывода, иначе она возвратится немедленно, поэтому мы можем для сигнализации использовать эту функцию вместо функций WaitFor’x’Objects.

возвраты — мы получаем булевое значение — мы должны вызвать GetLastError, чтобы проверить результат операции.

Прерываемый ввод/вывод

Чтобы понять прерываемый ввод/вывод, вы должны знать, что существует очередь APC. APC расшифровывается как асинхронный вызов процедуры. APC создается для каждого треда. Мы должны послать запрос на асинхронную операцию ввода/вывода и сохранить ее результаты в APC. Чтобы сделать это, мы должны использовать функцию ReadFileEx или WriteFileEx. У этих практически те же параметры, что и у их аналогов без приставки ‘Ex’. Основное изменение состоит в том, что необходимо указать функцию обратного вызова.

Итак, я упомянул функции ReadFileEx и WriteFileEx.

 push указатель на функцию обратного вызова
 push указатель на структуру OVERLAPPED
 push указатель на количество прочтенных байтов
 push количество байтов, которое нужно прочитать
 push указатель на буфер, куда будет производиться чтение
 push хэндл устройства, из которого будет производиться чтение
 call ReadFile

 push указатель на функцию обратного вызова
 push указатель на структуру OVERLAPPED
 push указатель на количество записанных байтов
 push количество байтов, которое нужно записать
 push указатель на буфер, из которого будет браться информация для записи
 push хэндл устройства, в которое будет производиться запись
 call WriteFile

Прототип функции обратного вызова (определение взято из MSDN).

 VOID WINAPI FileIOCompletionRoutine(DWORD fdwError,
             DWORD cbTransfered,LPOVERLAPPED lpo);

Я думаю, что назначение параметров достаточно ясно…хех.

И немного о прерываемом состоянии. Тред находится в прерваемом состоянии, если он вызвал одну из пяти функций:

  • WainForSingleObjectEx
  • WaitForMultipleObjectEx
  • SleepEx
  • SignalObjectAndWait
  • MsgWaitForMultipleObjectEx

(смотри прототипы в конце каждой части статьи)

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

И, наконец, заключительные слова данной части. Вы запрашиваете выполнение операции асинхронного ввода/вывода с помощью функций ReadFileEx или WriteFileEx, затем вы делаете еще что-нибудь. Когда вам нужно получить результаты операции, вы должны вызвать какую-нибудь API-функцию, чтобы установить тред в прерываемое состояние и если в APC находится выполненная операция ввода/вывода, будет вызывана функция обратного возврата. Вот и все…

Прототипы, которые я обещал…

 push булевое значение - прерываемость
 push время ожидания
 push хэндл ожидаемого объекта
 call WaitForSingleObjectEx

 push булевое значение - прерываемость
 push время ожидания
 push булевое значение - ждать все объекты
 push указатель на массив хэндлов объектов
 push количество объектов, которые нужно ждать
 call WaitForMultipleObjectEx

 push булевое значение - прерываемость
 push время ожидания
 call SleepEx

 push булевое значение - прерываемость
 push время ожидания
 push хэндл объекта, который нужно ждать
 push хэндл объекта для сигнализации
 call SignalObjectAndWait

 push флаги
 push маска пробуждения
 push время ожидания
 push указатель на ожидаемые хэндлы
 push количество объектов, которые нужно ждать
 call MsgWaitForMultipleObjectEx

Дополнение

Есть еще одна функция, которую следует обсудить. Это QueueUserAPC. Она позволяет установить новую запись в APC. У этой функции следующий прототип:

 push данные
 push хэндл треда, в очередь которого помещается запись
 push указатель на функцию обратного вызова
 call QueueUserAPC

У функции обратного вызова следующий прототип (взят из MSDN):

 VOID WINAPI APCFunc(DWORD dwParam)

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

Событийная сигнализация

Чтобы понять событийную сигнализацию, вам нужно быть знакомым с событиями. Если нет, вы, вероятно, не поймет следующие слова. Как бы то ни было, я оставляю это на вас. Хорошо, если вы взглянете на структуру OVERLAPPED, вы увидите двойное слво под названием _eventHandle. Он заполнено хэндлом события. Поэтому вы заполняете это поле хэндлом события, а затем запрашиваете операцию асинхронного ввода/вывода. Затем, если вы хотите получить результат вашей операции ввода/вывоа, вы просто ждете его с помощью какой-нибудь соответствующей функции (например, WaitForSingleObject). Эта и другие необходимые функции объяснены где-то в этой статье, поэтому я больше не буду этого касаться. И, напоследок, заключительные слова: наслажайтесь событийной сигнализацией :).

[C] mort[MATRiX], пер. Aquila

 

Источник wasm.ru /27.06.2002/

Поделиться в соц сетях

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Есть идеи, замечания, предложения? Воспользуйтесь формой Обратная связь или отправьте сообщение по адресу replay@sciencestory.ru
© 2017 Истории науки. Информация на сайте опубликована в ознакомительных целях может иметь ограничение 18+