Асинхронный ввод/вывод

Дата публикации 27 июн 2002

Асинхронный ввод/вывод — Архив WASM.RU

Предисловие

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

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

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

Код (Text):
  1.  
  2.   push указатель на структуру OVERLAPPED
  3.   push указатель на количество прочтенных байтов
  4.   push количество байтов, которое нужно прочитать
  5.   push указатель на буфер, куда будет производиться чтение
  6.   push хэндл устройства, из которого будет производиться чтение
  7.   call ReadFile
  8.  
  9.   push указатель на структуру OVERLAPPED
  10.   push указатель на количество записанных байтов
  11.   push количество байтов, которое нужно записать
  12.   push указатель на буфер, из которого будет браться информация для записи
  13.   push хэндл устройства, в которое будет производиться запись
  14.   call WriteFile

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

Код (Text):
  1.  
  2.  overlapped struc
  3.          _internal           dd ?
  4.          _internalHigh       dd ?
  5.          _offset             dd ?
  6.          _offsetHigh         dd ?
  7.          _eventHandle        dd ?
  8.  overlapped ends
  9.  _ol     overlapped <>

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

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

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

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

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

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

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

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

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

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

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

Код (Text):
  1.  
  2.  push время ожидания
  3.  push хэндл, которого ждем
  4.  call WaitForSingleObject

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

Код (Text):
  1.  
  2.  push время ожидания
  3.  push булевое значение
  4.  push указатель на поле хэндлов
  5.  push количество проверяемых объектов - максимально 64
  6.  call WaitForMultipleObjects

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

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

Код (Text):
  1.  
  2.  WAIT_OBJECT_0  = 0    - то, чего мы ждали - объект был просигнализирован
  3.                        - при использовании API XMultipleX при ожидании
  4.                           одного объекта, мы получаем индекс этого хэндла
  5.  WAIT_TIMEOUT   = 0102h - время ожидания истекло, объект не был
  6.                           просигнализирован
  7.  WAIT_FAILED    = -1    - произошла ошибка, используйте GetLastError для
  8.                           получения расширенной информации

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

Код (Text):
  1.  
  2.  push булевое значение - ожидание
  3.  push указатель на двойное слово - получаем байты перемещения
  4.  push указатель на структуру OVERLAPPED ввода/вывода, который мы проверяем
  5.  push handle
  6.  call GetOverlappedResult

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

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

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

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

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

Код (Text):
  1.  
  2.  push указатель на функцию обратного вызова
  3.  push указатель на структуру OVERLAPPED
  4.  push указатель на количество прочтенных байтов
  5.  push количество байтов, которое нужно прочитать
  6.  push указатель на буфер, куда будет производиться чтение
  7.  push хэндл устройства, из которого будет производиться чтение
  8.  call ReadFile
  9.  
  10.  push указатель на функцию обратного вызова
  11.  push указатель на структуру OVERLAPPED
  12.  push указатель на количество записанных байтов
  13.  push количество байтов, которое нужно записать
  14.  push указатель на буфер, из которого будет браться информация для записи
  15.  push хэндл устройства, в которое будет производиться запись
  16.  call WriteFile

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

Код (Text):
  1.  
  2.  VOID WINAPI FileIOCompletionRoutine(DWORD fdwError,
  3.              DWORD cbTransfered,LPOVERLAPPED lpo);

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

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

  • WainForSingleObjectEx
  • WaitForMultipleObjectEx
  • SleepEx
  • SignalObjectAndWait
  • MsgWaitForMultipleObjectEx

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

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

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

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

Код (Text):
  1.  
  2.  push булевое значение - прерываемость
  3.  push время ожидания
  4.  push хэндл ожидаемого объекта
  5.  call WaitForSingleObjectEx
  6.  
  7.  push булевое значение - прерываемость
  8.  push время ожидания
  9.  push булевое значение - ждать все объекты
  10.  push указатель на массив хэндлов объектов
  11.  push количество объектов, которые нужно ждать
  12.  call WaitForMultipleObjectEx
  13.  
  14.  push булевое значение - прерываемость
  15.  push время ожидания
  16.  call SleepEx
  17.  
  18.  push булевое значение - прерываемость
  19.  push время ожидания
  20.  push хэндл объекта, который нужно ждать
  21.  push хэндл объекта для сигнализации
  22.  call SignalObjectAndWait
  23.  
  24.  push флаги
  25.  push маска пробуждения
  26.  push время ожидания
  27.  push указатель на ожидаемые хэндлы
  28.  push количество объектов, которые нужно ждать
  29.  call MsgWaitForMultipleObjectEx

Дополнение

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

Код (Text):
  1.  
  2.  push данные
  3.  push хэндл треда, в очередь которого помещается запись
  4.  push указатель на функцию обратного вызова
  5.  call QueueUserAPC

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

Код (Text):
  1.  
  2.  VOID WINAPI APCFunc(DWORD dwParam)

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

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

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


0 1.244
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532