Здавалка
Главная | Обратная связь

Завершение процесса



В приведенном ниже списке перечислены основные этапы завершения процесса:

1. Прекращение выполнения потоков процесса.

2. Уничтожение User- и GDI-объектов процесса.

3. Закрытие объектов ядра процесса.

4. Переход объект ядра «процесс» в свободное состояние.

5. Закрытие объекта ядра «процесс».

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

Когда все потоки процесса завершаются самостоятельно, следует говорить о естественном завершении процесса. Только естественное завершение гарантирует корректную отчистку всех ресурсов процесса.

Процесс завершается досрочно, когда один из его потоков вызывает функцию ExitProcess. Вызов функции TerminateProcess завершает указанный процесс. Указанные функции форсируют немедленное уничтожение процесса, а это может помешать правильной очистке ресурсов (например, вызов деструкторов объектов, удаление временных файлов и т.д.) [3, 76 с].

Дочерние процессы

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

STARTUPINFO startInfo = { sizeof(startInfo) };

PROCESS_INFORMATION procInfo;

 

// Создание дочернего процесса

if (CreateProcess(..., &procInfo) == TRUE)

{

CloseHandle(procInfo.hThread);

 

// Ожидание завершения дочернего процесса

WaitForSingleObject(procInfo.hProcess, INFINITE);

 

// Определение кода завершения дочернего процесса

DWORD exitCode;

GetExitCodeProcess(procInfo.hProcess, &exitCode);

 

CloseHandle(procInfo.hProcess);

}

Потоки

Потоки всегда создаются в контексте какого-либо процесса, то есть исполняют код и манипулируют данными в адресном пространстве процесса. В связи с этим, потоки одного процесса могут исполнять один и тот же код и манипулировать одними и теми же данными, а также совместно использовать дескрипторы объектов ядра, поскольку таблица дескрипторов создается для процесса в целом.

Каждый поток связан с некоторой входной функцией. Для первичного потока таковой является main или WinMain. Входная функция вторичного потока должна иметь следующий прототип:

DWORD WINAPI ThreadFunc(PVOID param);

Таким образом, через параметр param потоку можно передавать некоторые данные, необходимые для его выполнения. По завершению входной функции потока происходит освобождение памяти, которая была отведена под его стек, а счетчик пользователей объекта ядра «поток» уменьшается на 1.

Создание вторичного потока осуществляется вызовом функции CreateThread:

HANDLE CreateThread(

PSECURITY_ATTRIBUTES sa,

DWORD stackSize,

PTHREAD_START_ROUTINE startAddress,

PVOID param,

DWORD creationFlags,

PDWORD threadId);

В приведенном ниже списке перечислены основные этапы создания потока:

1. Создание стека потока.

2. Создание и инициализация объекта ядра «поток».

3. Установка указателя на точку входа потока, запуск потока.

После создания объекта ядра «потока», счетчик числа его пользователей равен 1, но после того, как CreateThread возвращает управление вызывающей программе, его значение увеличивается до 2, так как результатом работы CreateThread является дескриптор созданного потока. Поэтому, если нет необходимости хранить ссылку на поток, сразу же после его создания следует закрывать полученный дескриптор вызовом функции CloseHandle (п. 3.5):

// Входная функция потока

DWORD WINAPI LoadFileThreadFunc(PVOID param)

{

// Определение входных параметров

char* fileName = (char*)param;

 

...

 

return 0;

}

 

void LoadFile(char* fileName)

{

DWORD threadId;

// Создание нового потока

HANDLE hThread = CreateThread(NULL, 0, LoadFileThreadFunc,

(PVOID)fileName, 0, &threadId);

// Закрытие дескриптора на неиспользуемый объект

CloseHandle(hThread);

}

При создании потока можно указать атрибуты защиты (п. 3.3) и определить, какую часть stackSize адресного пространства процесса поток может использовать под свой стек.

Параметр startAddress определяет адрес входной функции потока, а param – значение ее аргумента. Благодаря этому в функцию потока могут быть переданы любые данные, необходимые для ее инициализации.







©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.