ЭКСПЕРИМЕНТ: исследуем IRP
B этом эксперименте вы найдете незавершенные IRP в системе и определите тип IRP, устройство, которому он адресован, драйвер, управляющий этим устройством, поток, выдавший IRP, и процесс, к которому относится данный поток.
B любой момент в системе есть хотя бы несколько незавершенных IRR Это вызвано тем, что существует много устройств, которым приложения могут посылать IRP, а драйвер обрабатывает запрос только при возникновении определенного события, скажем, при появлении данных. Один из примеров – чтение с сетевого устройства. Увидеть незавершенные IRP в системе позволяет команда
Строка, выделенная в выводе, описывает IRP, адресованный драйверу Kbdclass, так что этот IRP скорее всего был выдан потоком необработанного ввода для подсистемы Windows, принимающим ввод с клавиатуры. Изучение IRP с помощью команды
Активный блок стека (помечаемый префиксом «›», находится в самом низу. Основной номер функции равен 3, что соответствует IRP_MJ_READ.
Следующий шаг – выяснить, какому объекту «устройство» адресован IRP Для этого выполните команду
Устройство, которому адресован данный IRP, – KeyboardClassl. Наличие объекта «устройство», принадлежащего драйверу Termdd, сообщает, что этот объект представляет ввод от клиента службы терминалов, а не с физической клавиатуры. (Листинг был получен в системе с Windows XP.)
Детальные сведения о потоке и процессе, выдавшем этот IRP, можно просмотреть командами
Найдя этот поток в Process Explorer (
После того как драйвер диска завершает передачу данных, диск генерирует прерывание, и ввод-вывод завершается (рис. 9-17).
Рис. 9-17.
B качестве альтернативы повторному использованию единственного IRP файловая система может создать группу
Драйвер файловой системы передает сопоставленные IRP драйверу устройства, который ставит их в очередь устройства. Они обрабатываются по одному, а файловая система отслеживает возвращаемые данные. Когда выполнение всех сопоставленных IRP заканчивается, подсистема ввода-вывода завершает обработку исходного IRP и возвращает управление вызывающему потоку (рис. 9-19).
Создание высокопроизводительного серверного приложения требует реализации эффективной модели многопоточности. Как нехватка, так и избыток серверных потоков, обрабатывающих клиентские запросы, приведет к проблемам с производительностью. Например, если сервер создает единственный поток для обработки всех запросов, клиенты будут «голодать», так как сервер будет обрабатывать по одному запросу единовременно. Конечно, единственный поток мог бы обрабатывать сразу несколько запросов, переключаясь с одной операции ввода-вывода на другую. Однако такая архитектура крайне сложна и не позволяет использовать преимущества многопроцессорных систем. Другая крайность – создание сервером огромного пула потоков, когда для обработки чуть ли не каждого клиентского запроса выделяется свой поток. Этот сценарий обычно ведет к перегрузке процессоров потоками: множество потоков пробуждается, выполняет обработку данных, блокируется в ожидании ввода-вывода, а после обработки запроса снова блокируется в ожидании нового запроса. Одно только наличие слишком большого количества потоков заставило бы планировщик чрезмерно часто переключать контекст, и в итоге он отобрал бы на себя немалую часть процессорного времени.
Задача сервера – свести к минимуму число переключений контекста, чтобы избежать излишнего блокирования потоков, и в то же время добиться максимального параллелизма в обработке за счет множества потоков. C этой точки зрения идеальна ситуация, при которой к каждому процессору подключен один активно обрабатывающий клиентские запросы поток, что позволяет обойтись без блокировки потоков, если на момент завершения обработки текущих запросов их ждут другие запросы. Однако такая оптимизация требует, чтобы у приложения была возможность активизировать другой поток, когда поток, обрабатывающий клиентский запрос, блокируется в ожидании ввода-вывода (например, для чтения файла в процессе обработки).
Приложения используют объект
Создавая порт завершения, приложение указывает максимальное число сопоставленных с портом потоков, которые могут быть активны. Как уже говорилось, в идеале на каждом процессоре должно быть по одному активному потоку. Windows использует это значение для контроля числа актив
ных потоков приложения. Если число активных потоков, сопоставленных с портом, равно заданному максимальному значению, выполнение потока, ждущего на порте завершения, запрещено. По завершении обработки текущего запроса один из активных потоков проверяет, имеется ли в очереди порта другой пакет. Если да, он просто извлекает этот пакет из очереди и переходит к обработке соответствующих данных; контекст при этом не переключается.