Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
195
Добавлен:
20.02.2016
Размер:
135.68 Кб
Скачать

[12.3] Создание потоков драйвером

В случае, когда использование системных рабочих потоков невозможно, драйвер должен создать свой собственный поток. Для создания нового потока используется функция PsCreateSystemThread(). В качестве одного из параметров функция имеет описатель процесса, в контексте которого нужно создать поток. Чтобы правильно использовать описатель, код драйвера должен выполняться в контексте процесса, таблица описателей которого содержит описатель процесса, в контексте которого мы хотим создать поток. Если описатель процесса не указан (значение NULL), новый поток будет создан в контексте процесса System.

Для уничтожения потока из драйвера используется функция PsTerminateSystemThread(). Эта функция должна быть вызвана из самого уничтожаемого потока, т.к. она уничтожает текущий поток и не позволяет указать поток, который нужно уничтожить.

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

Код потока может не только изменить значение приоритета планирования при уровне IRQL PASSIVE_LEVEL, но и повысить уровень IRQL. Для этого служит функция KeRaiseIrql(). Работа потока на повышенном уровне IRQL должна быть завершена как можно скорее, после чего должно быть восстановлено первоначальное значение IRQL с помощью функции KeLowerIrql(). Использование функции KeRaiseIrql() для понижения IRQL, и функции KeLowerIrql() для повышения IRQL не допускается, т.к. это приведет возникновению синего экрана.

[12.4] Потоки как диспетчерские объекты

Как говорилось на лекции 11, посвященной механизмам синхронизации, поток является диспетчерским объектом, который переходит в сигнальное состояние при своем завершении. Следующий пример демонстрирует способ синхронизации с помощью объекта-потока.

NTSTATUS DriverEntry( .... )

{

...

status = PsCreateSystemThread(&thread_handle,

0,

NULL,

0,

NULL,

thread_func,

pDevExt->thread_context);

if(status != STATUS_SUCCESS)

{

//обработка ошибки

}

else

{

status = ObReferenceObjectByHandle(thread_handle,

THREAD_ALL_ACCESS,

NULL,

KernelMode,

(PVOID*)&pDevExt->pThreadObject,

NULL);

if (status != STATUS_SUCCESS)

{

//обработкаошибки

}

}

...

}

Функцияпотока

VOID thread_func(PVOID Context)

{

//Рабочий код потока

...

//Завершениепотока

PsTerminateSystemThread(STATUS_SUCCESS);

}

Функция, ожидающая завершение работы потока:

.... SomeFunc( .... )

{

status = KeWaitForSingleObject(pDevExt->pThreadObject,

Executive,

KernelMode,

FALSE,

NULL);

ObDereferenceObject(pDevExt->pThreadObject);

...

}

Прокомментируем этот пример. При создании потока с помощью функции PsCreateSystemThread() возвращается описатель потока в контексте процесса, в котором поток был создан. Важно понимать, что это может быть совершенно не тот процесс, в контексте которого была вызвана функция PsCreateSystemThread(). В этом случае мы не можем напрямую воспользоваться функцией ObReferenceObjectByHandle() для получения указателя на объект-поток по его описателю.

Рис. 1

Существует простейший способ решения этой проблемы, основанный на том факте, что функция – точка входа в драйвер DriverEntry всегда вызывается в контексте потока System. Вызов функции PsCreateSystemThread() следует производить из DriverEntry, и при этом указывать создавать поток в контексте процесса System. Получив описатель созданного потока, можно получить указатель на объект-поток с помощью ObReferenceObjectByHandle(), и в дальнейшем пользоваться этим указателем в контексте любого процесса и потока. При завершении использования объекта-потока обязательно освободить его с помощью вызова ObDereferenceObject(). Все вышесказанное иллюстрируется рисунком 1.

Если драйверу все же необходимо по каким-либо причинам создать поток в контексте процесса, отличного от процесса System, либо создать поток в контексте процесса System, находясь в контексте другого потока, ему нужно каким-то образом попасть в контекст памяти процесса, в таблице описателей которого хранится информация о нужном процессе. Для этого служит недокументированная функция KeAttachProcess(). После необходимой обработки необходимо восстановить предыдущее состояние с помощью вызова KeDetachProcess().

Примечание.

Вышесказанное относилось только к ОС Windows NT 4.0. В ОС Win2K появилась специальная таблица описателей, называемая таблицей описателей ядра (kernel handle table), которая может быть доступна с помощью экспортируемого имени ObpKernelHandleTable. Таблица доступна только из режима ядра, при этом у всех описателей старший бит установлен в 1, так что значения всех описателей превышают 0x80000000. Как быть с уникальностью описателей для каждого процесса, на момент написания данной работы неясно и нуждается в исследовании.

Соседние файлы в папке Лабы по драйверам