Реализация процессов и потоков
Изображенные на рис. 5.5 структуры, за исключением блоков переменных окружения потока (TEB), существуют в системном адресном пространстве. Помимо этого, параллельная структура для каждого потока, созданного в Win32-процессе, поддерживается процессом Csrss подсистемы Win32. В свою очередь, часть подсистемы Win32, работающая в режиме ядра (Win32k.sys), поддерживает для каждого потока структуру W32THREAD.
Блок потока ядра KTHREAD содержит информацию, необходимую ядру для планирования потоков и их синхронизации с другими потоками. Просмотр структур данных потока может быть осуществлен отладчиком. Более подробно данный материал изложен в книге [ Руссинович ] .
Создание потоков
Создание потока инициируется Win32-функцией CreateThread, которая находится в библиотеке Kernel32.dll. При этом создается объект ядра "поток", хранящий статистическую информацию о создаваемом потоке. В адресном пространстве процесса выделяется память под пользовательский стек потока. Затем инициализируется аппаратный контекст потока (ниже имеется описание соответствующей структуры CONTEXT).
Вслед за этим создается блок управления потоком вместе с сопутствующими структурами, формируется стек ядра потока и о создании потока уведомляется подсистема Win32. Наконец, вызывающему потоку возвращается описатель создаваемого потока и передается управление, а новому потоку может быть выделено процессорное время.
Функция CreateThread
Таким образом, если первичный поток процесса создается при вызове функции CreateProcess, то для создания дополнительных потоков нужно вызывать функцию CreateThread:
HANDLE CreateThread ( PSECURITY_ATTRIBUTES psa, DWORD cbStack, PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam, DWORD fdwCreate, PDWORD pdwThreadID);
Прогон программы создания потока
Программа, листинг которой приведен ниже, создает новый поток и передает ему параметр, числовое значение которого этот поток выводит на экран.
#include <windows.h> #include <stdio.h> DWORD WINAPI MyThread( LPVOID lpParam ) { printf("Parameter = %d\n", *(DWORD*)lpParam); return 0; } VOID main( VOID ) { DWORD ThreadId, ThreadParameter = 10; HANDLE hThread; hThread = CreateThread( NULL, // атрибуты безопасности по умолчанию 0, // размер стека по умолчанию MyThread , // указатель на процедуру создаваемого потока &ThreadParameter, // аргумент, передаваемый функции потока 0, // флаги создания по умолчанию &ThreadId); // возвращаемый идентификатор потока if (hThread == NULL) printf("CreateThread failed." ); getchar(); CloseHandle( hThread ); }
В качестве самостоятельного упражнения рекомендуется написать программу, иллюстрирующие простоту организации межпотокового обмена в рамках одного процесса, например, обмен через набор общих глобальных данных. Сравните данный способ с более громоздкими примерами из лекции "Межпроцессный обмен".
Завершение потока можно организовать разными способами, например, с помощью функций ExitThread или TerminateThread. Рекомендуемый [ Рихтер ] способ - возврат управления функцией потока. Это единственный способ, который гарантирует корректную очистку всех ресурсов, принадлежавших потоку.
Подобно процессам при завершении потока сопоставленный с ним объект ядра "поток" не освобождается до тех пор, пока не будут закрыты все внешние ссылки на этот объект.
Контекст потока, переключение контекстов
Особую роль в структурах данных, описывающих потоки, играет контекст потока. Информацию, входящую в состав контекста, необходимо периодически сохранять и восстанавливать в случае возникновения различных событий, например, при переключении потоков. Обычно сохранению и последующему восстановлению подлежат:
- программный счетчик, регистр состояния и содержимое остальных регистров процессора;
- указатели на стек ядра и пользовательский стек;
- указатели на адресное пространство, в котором выполняется поток (каталог таблиц страниц процесса).
Эта информация сохраняется в текущем стеке ядра потока.
Контекст отражает состояние регистров процессора на момент последнего исполнения потока и хранится в структуре CONTEXT, определенной в заголовочном файле WinNT.h. Элементы этой структуры соответствуют регистрам процессора, например, для процессоров x86 процессоров в ее состав входят Eax, Ebx, Ecx, Edx и т д.. Win32-функция GetThreadContext позволяет получить текущее состояние контекста, а функция SetThreadContext - задать новое содержимое контекста. Перед этой операцией поток рекомендуется приостановить.
Помимо перечисленных в системе имеется много полезных функций, реализующих API для управления потоками. Их полный перечень содержится в MSDN.
Заключение
Поток представляет собой набор исполняющихся команд для текущего момента исполнения. С одним или несколькими потоками ассоциирован набор ресурсов, которые объединены в рамках процесса. Для описания процесса в системе поддерживается связанная совокупность структур, главной из которых является структура EPROCESS. В свою очередь, структура ETHREAD и связанные с ней структуры необходимы для реализации потоков. В лекции проанализированы функции CreateProcess и CreateThread и этапы создания процессов и потоков. Важными характеристиками потока являются его контекст и состояние. Наблюдение за состоянием потоков предлагается осуществить при помощи инструментальных средств системы.