Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 3000377.doc
Скачиваний:
29
Добавлен:
30.04.2022
Размер:
2.52 Mб
Скачать

3.9. Порты завершения ввода-вывода

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

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

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

Разрешить это противоречие на пользовательском уровне невозможно, поэтому в операционной системе Windows реализован специальный объект ядра – порт завершения ввода-вывода (I/O Completion Port), предназначенный для поддержания оптимального количества готовых к выполнению потоков в серверных приложениях.

Концепция использования портов завершения ввода-вывода состоит в следующем:

  • Сервер создает объект порт завершения ввода вывода посредством системного вызова CreateIoCompletionPort и связывает его с описателем объекта файл, через который будут доставляться запросы клиентов (например, именованный программный канал);

  • Сервер создает несколько потоков (пул потоков), которые будут обслуживать клиентские запросы (рекомендуется создать потоков, где – число процессоров);

  • Потоки обслуживания клиентских запросов выполняют цикл, в начале которого запрашивают данные клиентского запроса у порта завершения ввода-вывода посредством системного вызова GetQueuedCompletionStatus;

  • При отсутствии клиентских запросов потоки блокируются внутри системного вызова GetQueuedCompletionStatus;

  • Сервер создает один поток приема клиентских запросов, который выполняет цикл в теле которого инициирует чтение данных из файла, через который доставляются запросы клиентов;

  • Поток приема клиентских запросов блокируется на операции чтения файла до поступления клиентского запроса;

  • При поступлении клиентского запроса, запрос на чтение данных запроса, инициированный потоком приема клиентских запросов, будет завершаться в одном из потоков обработки клиентских запросов, который автоматически пробуждается для этого;

  • Система подсчитывает количество активных потоков обслуживания клиентских запросов, и активизирует новый поток при поступлении клиентского запроса только если число активных потоков не превышает заданное;

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

Работа порта завершения ввода-вывода реализована на базе следующих объектов: FILE_OBJECT, KTHREAD, KQUEUE, IO_COMPLETION, IO_COMPLETION_CONTEXT, IO_COMPLETION_PACKET, APC, взаимосвязи между которыми показаны на рис. .24.

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

Рис.24. Объекты, обеспечивающие функциональность порта завершения ввода-вывода

При этом потоки в очереди обслуживаются по дисциплине LIFO, а запросы – FIFO.

На пользовательском уровне типичное использование портов завершения ввода-вывода иллюстрируется следующим кодом.

Основной поток сервера:

HANDLE inPipe = CreateNamedPipe("\\\\.\\pipe\\имя", …); HANDLE ioComplPort = CreateIoCompletionPort(inPipe, NULL, Key, MaxCount); while(true) { ConnectNamedPipe(inPipe, NULL); char *buf = new char[sizeof(OVERLAPPED) + UserDataSize]; LPOVERLAPPED overlapped = (LPOVERLAPPED)buf; buf += sizeof(OVERLAPPED) ZeroMemory(overlapped, sizeof(OVERLAPPED)); ReadFile(inPipe, buf, UserDataSize, NULL, overlapped); DisconnectNamedPipe(inPipe); }

Поток обслуживания клиентских запросов:

DWORD numOfBytes; ULONG_PTR key; LPOVERLAPPED overlapped; while(true) { GetQueuedCompletionStatus(ioComplPort, &numOfBytes, &key, &overlapped, INFINITE); char *buf = (char*)(overlapped + 1) /////////////////////////////////////////////// // полезные действия delete overlapped;