Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lection 9_1

.doc
Скачиваний:
8
Добавлен:
13.04.2015
Размер:
141.31 Кб
Скачать

8

Глава 7. Управление процессами в среде ОС Windows

Процесс - это исполняемая программа, которая во время выполнения представлена двумя компонентами:

объектом ядра, через который ОС управляет процессом, и в котором хранится статическая информация о процессе;

адресным пространством, в котором содержится код и данные всех EXE и DLL модулей, стеки потоков и другая необходимая информация.

Для того, чтобы процесс мог работать, в нем должен быть минимум один поток, так как:

  • потоки отвечают за исполнение кода, содержащегося в адресном пространстве процесса;

  • один процесс может иметь несколько потоков, которые как бы одновременно исполняют код в адресном пространстве процесса, для этого каждый поток должен располагать собственным набором регистров;

  • если у процесса нет потоков, то система автоматически уничтожает его;

  • каждому потоку система отводит определённое процессорное время (отрезки времени, называемые квантами).

Windows 2000 в полной мере использует возможности машин с несколькими процессорами. Она способна закрепить каждый поток за отдельным процессором, и тогда два потока работают действительно одновременно. Ядра Win 9x/ME работают только с одним процессором, остальные простаивают.

В среде ОС Windows предоставляются возможности работы с процессами. Если нужно запустить новую программу, то нужно создать новый процесс. Для этого используется стандартный системный вызов CreateProcess. Однако пользо­ваться им не очень удобно. Приходится определять значение множества аргу­ментов. Для этой цели можно использо­вать менее сложный механизм.

Проще все­го использовать WinExec. В случае, если произошла ошибка, функция WinExec возвращает значение, мень­шее 32. Если программа успешно запущена, функция возвращает дескриптор но­вой программы (который не может быть меньше 32). После запуска новой программы функция WinExec немедленно передает управление программе, из которой был осуществлен вызов, то есть она не ждет момента, пока вновь запущенная про­грамма завершит работу,

Другой вариант запуска про­грамм, это использование API функции ShellExecute. Эта функция похожа на функцию WinExec. Она также поддерживает обработку типов файлов, зарегистрированных в операционной системе. Функцию ShellExecute можно использовать, например, для того, чтобы открыть Internet Explorer c необходимой ссылкой:

ShellExecute(Handle, nil, 'http://www.mysite.com', nil, nil, SW_SHOW);

Также эту функцию можно использовать для открытия соответствующего подкаталога для просмотра. Вот пример такого вызова:

ShellExecute(handle, "open", path, NULL, NULL, SW_SHOWNORMAL);

Параметр path - это путь к необходимому подкаталогу. В ответ на вызов этой функции будет открыт проводник Windows.

Функция ShellExecute имеет следующий синтаксис, ее параметры описаны в таблице 7.1:

ShellExecute( HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile,

LPCTSTR lpParameters, LPCTSTR lpDirectory,INT nShowCmd );

Таблица 7.1. Параметры функции ShellExecute

Параметр

Описание

Hwnd

Указатель на родительское окно, из которого осуществляется вызов функции.

LpOperation

Указатель на строку, которая указывает на операцию, которую нужно выполнить. Допустимы следующие параметры:

"open" - функция открывает файл, указанный в параметре lpFile. Файл может быть программой или другим файлом.

"print" будет печататься файл, указанный в lpFile. Если файл является программой, то он будет запущен.

"explore" функция вызовет окно для навигации по файлам и подкаталогам, начиная с того подкаталога, который указан в параметре lpFile.

Этот параметр может быть NULL. В этом случае функция будет открывать файл, указанный в lpFile.

LpFile

Указатель на строку, которая указывает на файл, который нужно открыть или напечатать.

LpParameters

Этот параметр содержит командную строку, если запускается исполняемый файл. Также этот параметр может быть NULL, если программа не требует параметров для запуска.

LpDirectory

Указатель на строку, которая содержит подкаталог по умолчанию для работы программы.

nShowCmd

Если параметр lpFile содержит исполняемый файл, то в этом параметре указывается режим отображения окна запущенной программы. SW_HIDE, SW_MAXIMIZE, SW_MINIMIZE, SW_RESTORE, SW_SHOW, SW_SHOWDEFAULT, SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED SW_SHOWMINNOACTIVE SW_SHOWNA SW_SHOWNOACTIVATE SW_SHOWNORMAL

Вызовы, наподобие ShellExecute и WinExec, удобно использовать для выпол­нения простейших действий вроде открытия файлов или запуска программ, контроль за действиями которых не имеет значения, или результаты их работы не влияют на вызывавшую программу. Если же есть необходимость создать новый процесс, используя при этом некоторые дополни­тельные параметры, то необходимо использовать функцию CreateProcess из Windows API, параметры которой приведены в таблице 7.3, а параметры, используемые при создании процесса приведены в таблице 7.4 .

Таблица 7.3 Параметры функции CreateProcess

Аргумент

Описание

lpApplicatlonName

Имя программы (или NULL, если имя программы указано в командной строке)

lpCommandLine

Командная строка

lpProcessAttributes

Атрибуты безопасности для дескриптора процесса, возвращаемого функцией

lpThreadAttributes

Атрибуты безопасности для дескриптора потока, возвращаемого функцией

bInheritHandlers

Указывает, наследует ли новый процесс дескрипторы, принадлежащие текущему процессу

dwCreationFlags

Параметры создания процесса

lpEnvironment

Значения переменных окружения (или NULL, в случае если наследуется текущее окружение)

lpCurrentDlrectory

Текущий каталог (или NULL, если используется текущий каталог текущего процесса)

lpStartupInfo

Структура STARTUPINFO, содержащая информацию о запуске процесса

lpProcessInformation

Возвращаемые функцией дескрипторы и идентификаторы ID процесса и потока

Таблица 7.4 Параметры, используемые при создании процесса

Флаг

Значение

CREATE_DEFAULT_ERROR_MODE

He наследовать текущий режим сообщений об ошибках

CREATE_NEW_CONSOLE

Создать новую консоль

CREATE_NEW_PROCESS_GROUP

Создать новую группу процессов

CREATE_SEPARATE_WOW_VDM

Запустить 16-битное приложение в его собственном адресном пространстве

CREATE_SHARED_WOW_VDM

Запустить 16-битное приложение в адресном пространстве общего доступа

CREATE_SUSPENDED

Создать процесс в приостановленном состоянии

CREATE_UNICODE_ENVIRONMENT

Блок переменных окружения записан в формате UNICODE

DEBUG_PROCESS

Запустить процесс в отладочном режиме

DEBUG_ONLY_THIS_PROCESS

Предотвратить отладку процесса текущим отладчиком (используется при отладке родительского процесса)

DETACHED_PROCESS

Новый консольный процесс не имеет доступа к консоли родительского консольного процесса

Аргумент lpStartupInfo — это указатель на структуру STARTUPINFO. Описание структуры приведено ниже.

typedef struct _STARTUPINFO {

DWORD cb;

LPTSTR lpReserved;

LPTSTR lpDesktop;

LPTSTR lpTitle;

DWORD dwX;

DWORD dwY;

DWORD dwXSize;

DWORD dwYSize;

DWORD dwXCountChars;

DWORD dwYCountChars;

DWORD dwFillAttribute;

DWORD dwFlags;

WORD wShowWindow;

WORD cbReserved2;

LPBYTE lpReserved2;

HANDLE hStdInput;

HANDLE hStdOutput;

HANDLE hStdError;

} STARTUPINFO, *LPSTARTUPINFO;

Параметр dwCreationFlags, определяющий какие свойства будет иметь создаваемый процесс. Параметр dwCreationFlags позволяет задать и класс приоритета с одним из возможных значений, указанных в таблице 7.6:

Таблица 7.6 Возможные значения параметра dwCreationFlags для задания приоритета

Значение

Класс приоритета

IDLE_PRIORITY_CLASS

Idle (простаивающий)

BELOW_NORMAL_PRIORITY_CLASS

Below normal (ниже обычного)

NORMAL_PRIORITY_CLASS

Normal (обычный)

ABOVE_NORMAL_PRIORITY_CLASS

Above normal (выше обычного)

HIGH_PRIORITY_CLASS

High (высокий)

REALTIME_PRIORITY_CLASS

Realtime (реального времени)

Поля этой структуры содержат заголовок консоли, начальный размер и позицию нового окна и перенаправление стандартных потоков ввода/вывода. Новая программа может проигнорировать все эти параметры в зависимости от того, как она написана. Поле dwFlags этой структуры содержит флаги, установленные в соответствии с тем, какие из остальных полей структуры хотелось бы использовать при запуске новой программы. Часто нет надобности использовать какие-либо из полей структуры STARTUPINFO, но все равно необходимо функции CreateProcess передать корректный указатель на пустую структуру STARTUPINFO. Также обязательно надо передать раз­мер структуры в поле cb и присвоить полю dwFlags значение 0.

Функция CreateProcess записывает в аргумент lpProcessInformation указатель на структуру, содержащую дескрипторы и идентификаторы ID нового процесса и по­тока. Доступ к этим дескрипторам определяется аргументами lpProcessAttributes и lpThreadAttributes.

Некоторые аргументы функции CreateProcess относятся к консольным приложениям. Другие имеют значение для всех типов программ. Зачастую при обращении к CreateProcess можно не заполнять структуру STARTUPINFO, но в любом случае необходимо передать функции указатель на существую­щую в памяти структуру, даже если эта структура не заполнена.

Функция CreateProcess возвращает значение типа BOOL. По завершении ее работы полезная для программиста информация размещается функцией в структуре типа PROCESS_INFORMATION, указатель на которую возвращается при помощи параметра lpProcessInformation этой функции. В структуре PROCESS_INFORMATION содержится идентификатор и дескриптор нового процесса, а также идентификатор и дескрип­тор самого первого потока, принадлежащего новому процессу. Эти сведения мо­гут использоваться для того, чтобы сообщить о новом процессе другим програм­мам, а также для того, чтобы контролировать новый процесс. Структура PROCESS_INFORMATION имеет следующее описание:

typedef struct _PROCESS_INFORMATION {

HANDLE hProcess;

HANDLE hThread;

DWORD dwProcessId;

DWORD dwThreadId;

} PROCESS_INFORMATION;

Следует помнить, что создание нового процесса влечёт за собой создание объектов ядра «процесс» и «поток», их счетчики числа пользователей инициализируются единицей. Функция CreateProcess (перед возвратом управления) открывает объекты ядра «процесс» и «поток», заносит их дескрипторы в элементы hProcess и hThread структуры PROCESS_INFORMATION и увеличивает числа пользователей до двух. Это значит, что перед тем как система сможет высвободить из памяти объект «процесс», процесс должен быть завершен (счетчик уменьшен до 1), а родительский процесс обязан вызвать функцию CloseHandle (обнулить счетчик), аналогично должно быть и с потоком. Созданному объекту ядра «процесс» (и «поток») присваивается уникальный идентификатор, ни у каких объектов этого типа не может быть одинаковых идентификаторов в системе. Автоматически присваиваемые идентификаторы могут повторно использоваться системой.

При создании процесса с использованием вызова CreateProcess можно пере­дать новому процессу дескрипторы открытых файлов. Однако это редко бывает полезным за исключением случаев, когда необходимо объединить стандартные потоки ввода/вы­вода нескольких консольных приложений. В этой ситуации дескрипторы файлов стандартных потоков ввода/вывода обладают заранее определенными значения­ми. Если новый процесс получает дескриптор открытого файла, он не может определить его значения, если только оно не определено за­ранее. Обладая дескриптором процесса, можно управлять процессом при помо­щи функций Windows API. Список этих функций приведен в таблице 7.7.

Таблица 7.7 Функции Windows API для управления процессом

Вызов

Назначение

Требуемый уровень доступа

CreateRemoteThread

Создает поток в другом процессе

PROCESS_CREATE_THREAD

GetExitCodeProcess

Возвращает код завершения процесса

PROCESS_QUERY_INFORMATION

GetGuiRecources

Определяет, сколько объектов USER или GDI (Graphical Device Interface) используется процессом

PROCESS_QUERY_INFORMATION

SetPriorityClass

Устанавливает базовый приоритет процесса

PROCESS_QUERY_INFORMATION

GetPriorityClass

Возвращает базовый приоритет, процесса

PROCESS_QUERY_INFORMATION

SetProcessAffinityMask

Определяет, какие из процессоров используются процессом в качестве основных

PROCESS_SET_INFORMATION

GetProcessAfflnityMask

Устанавливает, какие из процессоров используются процессом в качестве основных

PROCESS_QUERY_INFORMATION

SetProcessPriorityBoost

Позволяет или запрещает Windows динамически изменять приоритет процесса

PROCESS_SET_INFORMATION

GetProcessPriorityBoost

Возвращает статус изменения приоритета процесса

PROCESS_GET_INFORMATION

SetProcessShutDownParameters

Определяет, в каком порядке система закрывает процессы при завершении работы всей системы

Необходимо вызвать изнутри процесса

GetProcessShutDownParameters

Возвращает статус механизма завершения работы системы

PROCESS_QUERY_INFORMATION

SetProcessWorklngSetSize

Устанавливает минимальный и максимальный допустимый объем физической оперативной памяти, используемый процессом

PROCESS_SET_INFORMATION

GetProcessWorklngSetSize

Возвращает информацию об использовании физической памяти процессом

PROCESS_GET_INFORMATION

TerminateProcess

Корректное завершение работы процесса

PROCESS_TERMINATE

ExitProcess

Немедленное завершение процесса

Необходимо вызвать изнутри процесса

GetProcessVersion

Возвращает версию Windows, в среде которой хотел бы работать процесс

Использует ID процесса

GetProcessTimes

Возвращает степень использования CPU процессом

PROCESS_GET_INFORMATION

GetStartUpInfo

Возвращает структуру STARTUPINFO, переданную процессу при обращении к CreateProcess

Необходимо вызвать изнутри процесса

В таблице 7.7 указывается различный уровень доступа для различных функций для работы с процессами. Права доступа определяют точный набор возможностей, которые могут быть предоставлены или запрещены процессу, когда он пытается получить доступ к использованию объекта. Эти права доступа описываются так называемыми масками доступа, которые представляют собой 32-х битные числа. Возможные уровни доступа и их описание приведено в таблице 7.8.

Таблица 7.8. Уровни доступа и их описание

Значение уровня доступа

Описание

PROCESS_ALL_ACCESS

Предоставляет все возможные права доступа для объекта процесса

PROCESS_CREATE_PROCESS

Необходимый уровень доступа для создания процесса

PROCESS_CREATE_THREAD

Необходимый уровень доступа для создания потока

PROCESS_DUP_HANDLE

Необходимый уровень доступа для копирования указателя

PROCESS_QUERY_INFORMATION

Необходимый уровень доступа для получения некоторой информации о процессе, такой как класс приритета процесса

PROCESS_SET_INFORMATION

Необходимый уровень доступа для установки некоторой информации о процессе, такой как класс приоритета процесса

PROCESS_TERMINATE

Необходимый уровень доступа для завершения процесса

PROCESS_VM_OPERATION

Необходимый уровень доступа для осуществления операций над адресным пространством процесса

PROCESS_VM_READ

Необходимый уровень доступа для чтения памяти внутри процесса

PROCESS_VM_WRITE

Необходимый уровень доступа для записи в память внутри процесса

SYNCHRONIZE

Необходимый уровень доступа для ожидания завершения процесса

Дескриптор, возвращаемый функцией CreateProcess имеет значение PROCESS_ALL_ACCESS для уровня прав доступа к объекту процесса.

Необходимо помнить о том, что идентификатор ID-процесса не идентичен с дескрип­тором процесса. Дескрипторы процессов нельзя просто так передавать из процесса в процесс. Это означает, что если необходимо управлять процессом из другого процесса, то нужно получить дескриптор управляемого процесса. Для идентификации процесса другими процессами служит идентифика­тор ID этого процесса. Этот идентификатор можно передать из процесса в процесс. Преобразовать идентификатор ID-процесса в его дескриптор можно при помощи функции OpenProcess, но для этого нужно обладать необходимыми привилегиями. Определить идентификатор текущего процесса можно при помощи функ­ции GetCurrentProcessId. Эта функция позволяет узнать идентификатор собственного процесса и передать этот идентификатор другому процессу. Получив идентификатор ID процесса, другой процесс сможет открыть его дескриптор. При обращении к функции OpenProcess необходимо указать требуемый уровень доступа к открываемому процессу.

В примере показано использование функции CreateProcess, а также функции WaitForSingleObject. Функция WaitForSingleObject переводит программу в режим ожидания завершения запущенного процесса. Эта функция будет подробно рассмотрена в следующей главе.

Пример 7.1. Использование функции CreateProcess

FillChar(StartupInfo, SizeOf(StartupInfo), 0);

StartupInfo.cb := SizeOf(StartupInfo);

StartupInfo.dwFlags := STARTF_USESHOWWINDOW;

StartupInfo.wShowWindow := SW_Minimize; {SW_HIDE};

TheCommand := AppPath +“/i/p”;// формируется коммандная строка

if( not CreateProcess( nil, PChar(TheCommand),nil, nil, false,

0, nil, nil, StartupInfo, ProcInfo ))

then Beep; // Запуск процесса не удался

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

WaitForSingleObject(ProcInfo.hProcess, INFINITE);

// Закрываем дескрипторы процесса и потока

CloseHandle(ProcInfo.hProcess);

CloseHandle(ProcInfo.hThread);

Теперь рассмотрим аспекты, связанные с завершением процессов. Алгоритм завершения процесса состоит из таких этапов:

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

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

  3. Код завершения процесса меняется со значения STILL_ACTIVE на код, переданный в ExitProcess или TerminateProcess.

  4. Объект ядра «процесс» переходит в свободное состояние, или незанятое (signaled), состояние.

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

  6. Счётчик объекта ядра «процесс» уменьшается на 1.

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

BOOL GetExitCodeProcess(

HANDLE hProcess, // описатель процесса

PDWORD pdwExitCode // код его завершения

);

Если процесс не завершён, будет возвращено значение STILL_ACTIVE.

Процесс можно завершить следующими способами:

1. Входная функция первичного потока возвращает управление. Наиболее рекомендуемый способ завершения приложения (процесса). Это единственный способ, гарантирующий очистку всех ресурсов, принадлежавших первичному потоку: любые C++ объекты, созданные данным потоком, уничтожаются соответствующими деструкторами, ОС освобождает память, которую занимал стек потока, ОС устанавливает код завершения процесса (поддерживаемый объектом ядра «процесса») – его и возвращает входная функция, счетчик пользователей данного объекта ядра «процесс» уменьшается на 1.

2. Один из потоков процесса вызывает функцию ExitProcess. Процесс завершается, когда один из его потоков вызывает функцию ExitProcess. Эта функция завершает процесс и заносит значение в параметр код завершения процесса. Возвращаемое значение у функции отсутствует, так как результат её действия - завершение процесса. Код, стоящий после вызова этой функции никогда не выполнится. Когда входная функция возвращает управление, оно передаётся стартовому коду из библиотеки C/C++, и тот проводит очистку всех ресурсов, а затем обращается к ExitProcess, передавая ей значение, возвращённое входной функцией. Это приводит к тому, что будет пропущено освобождение ресурсов, выделенных объектам C++.

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

4. Поток другого процесса вызывает функцию TerminateProcess. Функция аналогична функции ExitProcess, но её основное отличие заключается в том, что она может быть вызвана из любого потока и завершает любой процесс.

BOOL TerminateProcess(

HANDLE hProcess, // дескриптор процесса

UINT fuExitCode // код завершения программы

);

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

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

В Windows 2000 и более поздних версиях существует такое понятие как рабочий набор. Рабочий набор процесса определяет, какой объем физической памяти диспетчер памяти Windows пытается сохранить за данным процессом. В рабочем наборе указывается минимальное и максимальное количество страниц памяти, которые должны принадлежать данному процессу. Узнать текущий размер можно при помощи вызова GetProcessWorkingSetSize. Если объем памяти, доступный для других приложений, становится неприемлемо маленьким, система может нарушить границы, установленные в рабочем наборе той или иной программы. Обладая необходимыми для этого привилегиями, программа может изменить собственный рабочий набор при помощи функции SetProcessWorkingSetSize. Если обе границы становятся равными 0xFFFFFFFF, то Windows сбрасывает на диск всю память, принадлежащую процессу.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]