- •Введение
- •Подсистема ввода-вывода: общие принципы построения и работы
- •1.1. Взаимодействие процессора с внешними устройствами
- •1.2. Прямой доступ к памяти
- •Драйверы
- •Роль драйверов в операционной системе
- •Взаимодействие драйверов с компонентами операционной системы и пользовательскими программами
- •Стек обработки запросов ввода-вывода
- •Основы организации и работы подсистемы ввода-вывода unix
- •2.1. Драйверы в операционных системах семейства unix
- •Стратегическая функция драйвера блочного устройства
- •Функция обработки прерывания
- •Функция опроса устройства
- •Другие функции драйверов
- •Буферизация в символьных драйверах
- •Терминальный драйвер
- •2.2. Потоковая подсистема ввода-вывода в unix
- •Архитектура и принципы работы подсистемы streams
- •Архитектура и работа модулей потока
- •Функция модуля put
- •Функция модуля service
- •Структура сообщения
- •Основы организации и работы подсистемы ввода-вывода windows
- •3.1. Классификаций драйверов Windows
- •Драйверы пользовательского режима
- •Драйверы режима ядра
- •3.2. Объекты подсистемы ввода-вывода
- •Объект файл
- •Объект устройство
- •Объект драйвер
- •Объект пакет запроса ввода-вывода
- •Объект блок стека запросов ввода-вывода
- •3.3. Передача данных между пользовательским адресным пространством и пространством ядра
- •Буферизированный ввод-вывод
- •Прямой ввод-вывод
- •Ввод-вывод под управлением драйвера
- •3.4. Обработка запросов ввода-вывода
- •Прохождение запроса ввода-вывода вниз через стек обработки запросов ввода-вывода
- •Обработка прерывания по завершению ввода-вывода
- •Обратное прохождение запроса ввода-вывода вверх через стек запросов ввода-вывода
- •3.5. Буферизация запросов ввода-вывода
- •Системная очередь запросов
- •Очереди запросов под управлением драйвера
- •3.6. Диспетчер Plug-And-Play, установка и запуск драйверов
- •3.7. Диспетчер электропитания
- •3.8. Среда сетевых драйверов ndis
- •Драйверы среды ndis Минипорт-драйверы сетевых адаптеров
- •Драйверы протоколов
- •Промежуточные драйверы
- •Структура ndis пакета
- •Запросы к сетевым адаптерам
- •3.9. Порты завершения ввода-вывода
- •Заключение
- •Библиографический список
- •Оглавление
- •394026 Воронеж, Московский просп., 14
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;