Иллюстрированный самоучитель по программированию систем защиты

Рабочие потоки

Необходимость в создании рабочих потоков

Любой исполняемый код, как и код драйвера, работает в контексте некоторого потока. Мы пока не обсуждали способы, с помощью которых драйвер может создать собственный поток, поэтому предполагается, что поток, в котором выполняется код драйвера, принадлежит некоторой прикладной программе. Это означает, что прикладная программа создала такой поток для выполнения своего кода, а не кода нашего драйвера. Если код драйвера производит длительную обработку, либо драйвер использует механизм синхронизации с ожиданием освобождения некоторого ресурса, код прикладной программы, для выполнения которого и создавался поток, не выполняется. Если этот поток единственный в прикладном процессе, то прикладная программа "висит".

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

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

Возможна другая ситуация, требующая обязательного создания потоков, когда драйверу необходимо выполнить операции на уровне IRQL меньшем DISPATCHJLEVEL, а код драйвера работает на повышенных уровнях IRQL, больших или равных DISPATCH_LEVEL.

Системные рабочие потоки

В процессе системной инициализации NT создает несколько потоков в процессе System. Эти потоки служат исключительно для выполнения работы, затребованной другими потоками. Такие потоки наиболее удобны в случаях, когда потоку с повышенным уровнем IRQL требуется выполнить работу на уровне IRQL PASSIVE_LEVEL.

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

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

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

Существует три типа системных рабочих потоков: Delayed (замедленные), Critical (критические) и Hypercritical (сверхкритические). Все типы потоков создаются на уровне IRQL PASSIVE_LEVEL. Для каждого типа потоков будут различны:

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

Число потоков каждого типа зависит от объема памяти и типа ОС. В таблице 10 указано число потоков и базовый приоритет планирования для ОС Win2000 Professional и Server.

Таблица 10. Число Системных Рабочих Потоков.

Тип рабочего потока Объем системной памяти Базовый приоритет планирования
12-19 MB 20-64 MB >64MB
Delayed 3 3 3 Значение в диапазоне динамических приоритетов
Critical 3 Professional: 3 Server: 6 Professional: 5 Server: 10 Значение в диапазоне приоритетов реального времени
HyperCritical 1 1 1 Не документирован

Следует отметить, что использование единственного потока типа HyperCritical не документировано. ОС использует этот поток для выполнения функции – чистильщика, которая освобождает потоки при их завершении.

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

Для работы с системными рабочими потоками существует два набора функций – функции с префиксом Ех, и функции с префиксом . Функции с префиксом Ех использовались в ОС NT 4.0 и более ранних версиях, и в Win2000 считаются устаревшими. В любом случае, вначале драйвер должен инициализировать рабочий элемент с помощью функций ExInitializeWorkltem() или IoAllocateWorkItem(), поместить рабочий элемент в очередь с помощью функций ExQueueWorkltem() или loQueueWorkltem(), а при запуске функции, указанной в рабочем элементе, эта функция обязана освободить занимаемые рабочим элементом ресурсы с помощью функций ExFreePool() или loFreeWorkltem().

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.