Потоки как диспетчерские объекты
Как говорилось в разделе, посвященном механизмам синхронизации, поток является диспетчерским объектом, который переходит в сигнальное состояние при своем завершении. Следующий пример демонстрирует способ синхронизации с помощью объекта-потока.
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() возвращается описатель потока в контексте
процесса, в котором поток был создан. Важно понимать, что это может быть
совершенно не тот процесс, в контексте которого была вызвана функция PsCreateSystem
Thread(). В этом случае мы не можем напрямую воспользоваться функцией
ObReference ObjectByHandle() для получения указателя на объект-поток по
его описателю.
Существует простейший способ решения этой проблемы, основанный на том
факте, что функция - точка входа в драйвер DriverEntry, всегда вызывается
в контексте потока System. Вызов функции PsCreateSystemThread() следует
производить из DriverEntry, и при этом указывать создавать поток в контексте
процесса System. Получив описатель созданного потока, можно получить указатель
на объект-поток с помощью ObReferenceObjectByHandle(), и в дальнейшем
пользоваться этим указателем в контексте любого процесса и потока. При
завершении использования объекта-потока надо обязательно освободить его
с помощью вызова ObDereferenceObject(). Все вышесказанное иллюстрируется
рис. 13.
Если драйверу все же необходимо по каким-либо причинам создать поток в
контексте процесса, отличного от процесса System, либо создать поток в
контексте процесса System, находясь в контексте другого потока, ему нужно
каким-то образом попасть в контекст памяти процесса, в таблице описателей
которого хранится информация о нужном процессе. Для этого служит недокументированная
функция KeAttachProcess(). После необходимой обработки необходимо восстановить
предыдущее состояние с помощью вызова KeDetachProcess().
Вышесказанное относилось только к ОС Windows NT 4.0. В ОС Win2000 появилась
специальная таблица описателей, называемая таблицей описателей ядра (kernel
handle table), которая может быть доступна с помощью экспортируемого имени
ObpKernelHandleTable. Таблица доступна только из режима ядра, при этом
у всех описателей старший бит установлен в 1, так что значения всех описателей
превышают 0x80000000. Как быть с уникальностью описателей для каждого
процесса, на момент написания данной книги неясно и нуждается в исследовании.
Рис. 13