Описатель, командная строка, и переменные окружения процесса
Любому .exe и .dll модулю, загруженному в адресное пространство процесса, присваивается специальная системная характеристика, которая называется описателем процесса. Для каждого процесса эта характеристика является уникальной, и передается в программу основной функцией первичного потока как первый параметр этой функции. Значение описателя используется в ходе выполнения процесса при вызовах из процесса системных функций, загружающих в память те или иные ресурсы. Конкретное значение описателя равно базовому адресу той части адресного пространства процесса, в которую загружается образ .exe или .dll файла. Значение этого адреса определяется компоновщиком на этапе создания исполняемого файла.
Для ряда широко используемых систем программирования этот адрес имеет фиксированное значение (0x00400000). Значение этого адреса для любого процесса можно получить, если в прикладной программе вызвать системную функцию GetModuleHandle (). Параметром этой функции является строка символов, описывающая полное имя .exe или .dll файла, и заканчивающаяся нулями.
При создании, новому процессу передается строка символов, которая называется командной строкой. Она представляет собой полное имя исполняемой программы входного потока. В момент запуска программы этого потока стартовая функция читает эту строку, после чего её содержимое используется при создании процесса. Кроме того, командная строка может анализироваться и использоваться в программе любого потока процесса. Её можно прочитать, используя системную функцию GetCommandLine (). Функция возвращает адрес командной строки.
С любым процессом связывается также структура данных, которая называется «блок переменных системного окружения». Физически, он представляет собой область памяти, в которой содержатся текстовые строки, описывающие переменные системного окружения процесса. Строки имеют следующий общий формат:
<VarName1>=<VarValue1>\0
Переменные окружения используются в программах потоков процесса для детальной настройки этих программ, в частности, в них указываются полные имена файлов, каких-либо иных программ или областей данных.
После запуска программы потока, в ней проверяется наличие и значения соответствующих переменных, и реализуется соответствующая настройка программы.
В системе существует исходный набор переменных системного окружения, который хранится в файле автозапуска autoexec.bat. Переменные, представленные в этом файле доступны всем процессам, которые запускаются в текущем сеансе работы ОС.
Для работы с переменными системного окружения имеются две системные функции:
· GetEnvironmentVariable ();
· SetEnvironmentVariable ().
Первая функция осуществляет чтение системного окружения процессу с помощью описателя, а с помощью второй осуществляется добавление, модификация, удаление переменных системного окружения.
Создание процессов.
Для создания любого процесса служит основное системное средство, а именно – системная функция CreateProcess (), которая вызывается в прикладной программе.
Прим. В существующих системах программирования имеются дополнительные средства создания процессов, представленные в виде соответствующих классов.
Функция CreateProcess имеет следующий формат вызова:
CreateProcess (P1, P2,…, P9);
В результате выполнения этой функции, система создает объекты ядра типа «процесс» с начальным значением параметра, который называется счетчиком числа пользователей процесса, равным единице. Затем, система выделяет для нового процесса область ВАП и загружает в него программный код и данные всех .exe и .dll файлов процесса. Затем формируется объект ядра типа «поток» для первичного потока нового процесса. Перед запуском программы этого потока выполняется стартовая функция системы программирования. Затем начинают выполняться программные коды основной функции первичного потока.
Функция CreateProcess имеет несколько параметров (9), назначение и смысл которых позволяют получить более подробную информацию о действиях системы при выполнении данной функции.
Значение и смысл параметров:
1. Представляет собой указатель на полную командную строку процесса. При выполнении функции CreateProcess эта строка читается и из неё выделяется имя исполняемого файла программы первичного потока. Если в имени этого файла полный путь доступа не указан, то функция осуществляет поиск файла, просматривая несколько каталогов:
1) каталог, содержащий .exe-файл запускаемого процесса;
2) текущий каталог создаваемого процесса;
3) системный каталог Windows;
4) основной каталог Windows;
5) каталоги, перечисляемые в файле автозапуска;
После нахождения нужного исполняемого файла, система выполняет уже упомянутые выше действия – создает объект типа «процесс», выделяет область ВАП, и т. д.;
2. Также и 3. Определяют атрибуты защиты объектов «процесс» и «поток». Они используются для дочернего процесса, который запускается из исходного. Эти атрибуты могут устанавливаться системой по умолчанию, либо могут задаваться пользователем с помощью двух системных структур данных;
3. -/-
4. Имеет логический тип и определяет возможность наследования в дочернем процессе описателя родительского процесса. Если true, то система передает дочерним процессам наследуемые описатели, и не передает в противном случае;
5. Задает флаги, которые определяют дополнительные свойства создаваемого процесса. Таких флагов несколько, и они могут комбинироваться с помощью операции «или»:
a) флаг, разрешающий в случае его установки выполнять отладку дочернего процесса, а также всех других процессов, которые могут быть порождены дочерним;
b) флаг, позволяющий в случае его установки создавать новый процесс и, в то же время, приостанавливать запуск его первичного потока. Это позволяет родительскому процессу изменять содержимое памяти в адресном пространстве дочернего процесса, а также изменять приоритет первичного потока дочернего процесса, и выполнять другие действия. После выполнения всех предварительных изменений запуск дочернего процесса может быть разрешен;
c) флаги (3), которые определяют порядок работы с консольными (текстовыми) окнами, создаваемыми в родительском или дочернем процессах;
d) флаги (2), определяющие порядок выполнения в системе шестнадцатиразрядных программ. Для таких программ в системе создается специальный режим работы, который называется виртуальной DOS-машиной (VDM). Флаги определяют, является ли эта машина одной для всех шестнадцатиразрядных программ, либо для каждой из них создается своя отдельная VDM. Кроме того, данный параметр позволяет задавать процессу один из шести возможных классов приоритета, а именно:
i) простаивающий;
ii) ниже обычного;
iii) обычный;
iv) выше обычного;
v) высокий;
vi) реального времени.
Классы приоритета влияют на распределение процессорного времени между процессами и потоками;
6. Представляет собой указатель на блок памяти, где хранятся строки переменных системного окружения;
7. Устанавливает текущий диск и текущий каталог для дочернего процесса;
8. Представляет собой указатель на системную структуру данных STARTUPINFO. Элементами этой структуры являются различные вспомогательные характеристики процесса, как то:
a) имя рабочего стола;
b) заголовок консольного окна;
c) координаты (x; y) для верхнего левого угла окна программы;
d) ширина и высота окна в пикселях;
9. Представляет собой указатель на системную структуру данных PROCESS_INFORMATION. В элементах этой структуры функция CreateProcess в ходе своего выполнения записывает описатели объектов ядра типа «процесс» и типа «поток», а также их идентификаторы. Эти параметры используются при завершении работы функции CreateProcess.
Завершение процесса.
Любой процесс в ОС Windows может быть завершен четырьмя следующими способами:
1. Путем возврата управления из основной функции первичного потока процесса;
2. Вызовом системной функции завершения процесса ExitProcess (), которая может вызываться в одном из потоков завершаемого процесса;
3. Вызовом функции уничтожения процесса TerminateProcess () в любом потоке другого процесса;
4. Одновременным выполнением во всех потоках данного процесса функции завершения потока ExitThread () или функции уничтожения потока TerminateThread ().
Наиболее рациональным и правильным является первый способ, поскольку он гарантирует нормальное освобождение всех занятых ресурсов, принадлежащих первичному потоку процесса. В этом случае, система выполняет следующие действия:
1. Все объекты и классы, созданные в потоке, уничтожаются соответствующими деструкторами;
2. Освобождается память, которую занимал стек первичного потока;
3. Устанавливается код завершения процесса, который возвращается из основной функции программы первичного потока;
4. Счетчик числа пользователей объекта ядра типа «процесс» уменьшается на единицу.
· Счетчик числа пользователей объекта ядра – это системный элемент данных, сопровождающий каждый объект ядра. Для объектов типа «процесс» и «поток» значения этого счетчика хранятся в информационной структуре PROCESS_INFORMATION. Назначение следующее: в момент создания объекта ядра, счетчику присваивается единица. Когда к существующему объекту ядра выполняется обращение, например, из другого процесса, состояние счетчика увеличивается на единицу. Когда какой-либо процесс завершается, то состояние счетчиков всех используемых им объектов ядра автоматически уменьшается на единицу. Как только состояние счетчика объекта ядра становится равным нулю, ядро уничтожает этот объект.
Способ второй. Функция ExitProcess завершает текущий процесс и принимает код его завершения из основной функции первичного потока. Возвращаемого значения эта функция не имеет, а выполняет лишь действие по завершению процесса. Данная функция вызывается и исполняется при нормальном завершении процесса в ходе выполнения стартовой функции, после того как первичный поток завершает свое выполнение. Самостоятельный вызов этой функции в любом потоке процесса возможен, но имеет особенности. Они состоят в том, что текущий процесс, в частности, его объект ядра, уничтожаются, но в то же время, выполнение программы основной функции может быть не завершено. В результате, все ранее созданные объектно-ориентированные элементы программы уничтожаются не их деструкторами, как должно быть при нормальном завершении, а их функционирование прекращается за счет внешнего воздействия. Такое завершение может привести к непредсказуемым ошибкам в системе. Поэтому применять функцию ExitProcess для самостоятельного применения процесса не рекомендуется.
Третий способ. Заключается в применении функции TerminateProcess ().
Отличие от ExitProcess() состоит в том, что данная функция выполняется в любом потоке другого процесса. С её помощью может завершаться не только текущий, но и произвольный процесс. Входным параметром этой функции является дескриптор завершаемого процесса, а выходным – код завершаемого процесса. Обычно, эта функция используется в крайних случаях, когда требуется экстренное завершение процесса. Как правило, действие этой функции оказывается внезапным для текущего процесса, поэтому он может не успеть выполнить все операции по нормальному завершению, как то освобождение ресурсов, закрытие объектов, сохранение промежуточных результатов во внешней памяти и т. д. Все эти операции выполняются системой, и в связи с этим, появляется вероятность потери промежуточных данных процесса.
Четвертый способ. Соответствует случаю, когда все потоки процесса одновременно выполняют функцию закрытия потока ExitThread (), либо потоки закрываются извне вызовом функции TerminateThread (). Обнаружив, что все потоки процесса не выполняются, система завершает и сам процесс с кодом завершения, равным коду завершения последнего потока процесса.
Во всех четырех случаях завершения процесса, в системе выполняются дополнительно следующие действия:
1. Выполнение всех потоков прекращается;
2. Все объекты, созданные процессом, уничтожаются, а объекты ядра закрываются, если их еще не используют другие процессы;
3. Формируется код завершения процесса;
4. Объект ядра «процесс» переходит в так называемое свободное состояние, и значение счетчика числа пользователей этого объекта уменьшается на единицу. Связанный с завершаемым процессом объект ядра полностью не освобождается до тех пор, пока не будут закрыты ссылки на него из других процессов, если эти ссылки создавались. Как только состояние счетчика числа пользователей объекта ядра становится равным нулю, объект полностью освобождается. Это событие произойдет тогда, когда все другие процессы, открывшие у себя дескриптор завершаемого процесса, вызовут функцию CloseHandle (), и тем самым сообщат системе о том, что ссылка на данный процесс больше не требуется.