Модель перекрытого ввода-вывода

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

В рассмотренных ранее моделях посылались некоторые уведомления (такие как «данные доступны» или «готов попробовать переслать данные» и т.д.) при возникновении определенных сетевых событий. Перекрывающие модели также посылают уведомления, но не о наступлении сетевых событий, а об их завершении. При вызове WinSock функции она может либо успешно завершиться, либо завершиться неудачей с кодом ошибки WSA_IO_PENDING. При использовании перекрывающих моделей Вы будете уведомлены о завершении операции. Это значит, что Вам просто надо подождать, пока операция не завершится.

Ценой этого эффективного подхода является трудная реализация. Если Вам не требуется действительно хорошая эффективность, то лучше воспользоваться ранее описанными моделями. Кроме того, операционные системы Windows 9x/ME не полностью поддерживают перекрытые модели ввода/вывода.

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

Перекрытый ввод/вывод: блокирование

Первая модель перекрытого ввода/вывода, о которой я расскажу, использует объект события для сигнализирования о завершении. Эта модель во многом похожа на WSAEventSelect, но отличием является то, что объект устанавливается в сигнализированное состояние при завершении WinSock операции, а не при наступлении какого-то сетевого события.

Программа: «Отправь-ка эти данные»

WinSock: «Окей, но я не могу отправить их прямо сейчас»

Программа ждет сигнала от объекта события, указывающего на то, что функция завершена

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

Перекрытый ввод/вывод: polling

Так же как и в ранее упомянутой модели polling, в этой модели так же можно запросить статус выполнения операции (хотя в ранее описанном polling’e мы не запрашивали статус выполнения, а просто получали данные о неудачном завершении функции. Но основной поток программы знал, когда функция завершилась неудачно, а когда наоборот). С помощью функции WSAGetOverlappedResult можно узнать статус выполняемой операции. Графическая интерпретация перекрытого polling’а очень похожа на интерпретацию обычного polling’а, за исключением того, что WinSock функция выполняется в то же время, что и опрос программы о выполнении функции.

Программа: «Отправь-ка эти данные»

WinSock: «Окей, но я не могу отправить их сейчас»

Программа: «Уже отправил?»

WinSock: «Нет»

Программа: «Уже отправил?»

WinSock: «Нет»

Программа: «Уже отправил?»

WinSock: «Нет»

Программа: «Уже отправил?»

WinSock: «Да!»

И тут я повторюсь: эта модель не очень хороша, так как она приводит процессор в панику. Поэтому я не рекомендую использовать эту модель.

Перекрытый ввод/вывод: процедуры завершения

Процедуры завершения – процедуры обратного вызова (т.е. вызываются в ответ на определенное действие. Далее я буду называть эти процедуры процедурами отзыва), которые вызываются при завершении операции. Тут вроде бы все просто, но есть одна хитрость: эти процедуры вызываются в контексте потока, который начал операцию. Что это значит? Представьте себе поток, который запросил перекрытую операцию записи. WinSock выполняет эту операцию, в то время как Ваш поток тоже выполняется. Таким образом у WinSock есть свой собственный поток для этой операции. Когда операция закончится, WinSock должен вызвать процедуру отзыва. Если это произойдет, то вызванная процедура будет выполняться в контексте потока WinSock. Это означает, что поток, вызвавший операцию записи, будет выполняться одновременно с процедурой вызова. Проблема состоит в том, что синхронизация с вызывающим потоком отсутствует, и он не знает, завершена ли операция (только если ему не сообщат об этом из параллельного потока).

Что бы избежать этого, WinSock удостоверяется в том, что процедура отзыва протекает в том же потоке, из которого происходил запрос. Осуществляется это с помощью APC (Asynchronous Procedure Call или Асинхронный Вызов Процедуры), механизма, встроенного в Windows. Это можно представить как «внедрение» процедуры в основной поток выполнения программы. Таким образом, поток сначала выполнит процедуру, а потом будет делать, то, что делал до ее «внедрения». Естественно, что система не может приказать потоку: «Прекрати делать все, что делал и обработай сначала эту процедуру».

Для того, что бы обеспечить «внедрение» в нужное место механизм APC требует, что бы поток находился в, так называемом, извещающем состоянии ожидания. Каждый поток имеет свою собственную APC очередь, в которой процедуры ждут своего вызова. Когда поток входит в извещающее состояние ожидания, это указывает на то, что он готов обслуживать очередь APC.

Перекрытый ввод/вывод с процедурами завершения использует APC для уведомления о завершении операции.

Программа: «Отправь-ка эти данные»

WinSock: «Окей, но я не могу отправить их сейчас»

Программа входит в извещающее состояние ожидания

Функция завершилась

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

Выполняется функция отзыва и управление переходит к программе

APC может быть немного трудным для понимания, но это не страшно. Это всего лишь краткое введение. Обычно поток находится в состоянии ожидания до тех пор, пока не будет вызвана процедура отзыва, которая обрабатывает событие (о том, что WinSock функция завершена) и возвращает управление программе. Потом главный поток производит необходимые операции (если таковые имеются) и возвращается в состояние ожидания снова.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: