Введение в POSIX'ивизм

       

Жизнь и смерть процесса


Каждый пользовательский процесс порождается каким-либо другим процессом - в конечном счете, в первооснове их всех лежит процесс init. Как нетрудно догадаться, порождающий процесс называется также родительским, или материнским, а порожденный - дочерним (по русски получается несоответствие грамматических родов, но "сыновний" звучит не очень привычно). И идентификатор родительского процесса (PPID) - также один из важных атрибутов пользовательского процесса, в чем мы немедленно и убедимся.

Каждый процесс в своем существовании проходит стадии зарождения, исполнения (часто с порождением новых процессов) и завершения (отмирания). Как все это выглядит в реальности, можно рассмотреть на примере самого первого пользовательского процесса, запускающего его командную оболочку по умолчанию (login shell). Для этого опять обратимся к выводу команды ps в форме

$ps l

для того, чтобы видеть идентификаторы родительских процессов (за их показ отвечает опция l). Дополнительно отфильтруем (что это такое, будет объясняться в ) процессы, относящиеся только к одной из виртуальных консолей, на которой в данный момент никто не авторизован - для определенности, второй по счету (устройство v1):

$ ps l | grep v1

Для пустой, то есть не несущей пользовательского сеанса, консоли вывод последней команды будет примерно таким (я опускаю столбцы, содержание которых в данный момент для нас не существенно):

UID PID PPID COMMAND 0 491 1 /usr/libexec/getty

То есть мы имеем в этой консоли единственный процесс getty, принадлежащий суперпользователю (UID 0), имеющий идентификатор 491 и порожденный процессом init (пример приведен для DragonFlyBSD, и потому идентификатор его равен единице).

После же авторизации вывод той же команды приобретет такой вид:

UID PID PPID COMMAND 0 491 1 login 1001 1949 491 -zsh

Он свидетельствует, что в нашей экспериментальной консоли запущено уже два процесса - login, показывающий, что на ней зарегистрирован некий пользователь, и командная оболочка последнего - zsh,

И еще прошу обратить внимание: PID процесса, ассоциированного с командой login, тот же, что и для процесса, исполнявшего перед этим команду getty, Это свидетельствует, что авторизация пользователя не привела к запуску нового процесса - напротив, замещение одной программы другой произошло в рамках одного и того же процесса (одна из причин, почему не следует отождествлять процесс и программу).
А вот идентификатор для zsh - другой: командная оболочка исполняется в порожденном (см. значение PPID) процессе.

Вытеснение одной программы другой в рамках единого процесса - типичный способ запуска новой программы. Так, если из командной строки zsh мы запустим, например, текстовый редактор joe, то в ответ на

$ ps l | grep v1

увидим следующую картину:

UID PID PPID COMMAND 0 491 1 login 1001 1949 491 -zsh 1001 2208 1949 joe

которая может создать впечатление, будто бы процесс zsh непосредственно породил процесс joe. Однако это не так. К сожалению, проиллюстрировать динамику зарождения процесса не представляется возможным (по крайней мере, я не знаю, как), поэтому прошу поверить на слово - не мне даже, а авторам фундаментальных руководств по Unix.

На самом деле родительский процесс перво-наперво порождает свою собственную копию - в данном случае еще один экземпляр командной оболочки zsh (не потому ли первый Shell Борна и получил свое имя, что вызов программы из оболочки напоминает последовательную серию скорлупок?). И различаются в момент зарождения родительский и дочерний процессы фактически только своими PPID (ну и собственными идентификаторами, разумеется). И лишь затем в рамках дочернего процесса осуществляет запуск на исполнение собственно нашей новой программы.

В свою очередь, новый процесс также может выступать в качестве родительского. Так, редактор joe обладает способностью запуска из своей среды программ оболочки. В образовавшемся имитаторе командной строки можно запустить какую-либо другую программу, скажем, поиск файла командой find. Если в процессе поиска посмотреть распределение процессов (например, с другой виртуальной консоли), можно будет увидеть, что процесс joe породил как бы свою копию - но уже с иными идентификаторами (при этом PPID копии, как и следовало ожидать, равен PID оригинала).

Как станет ясным из дальнейшего, понимание механизма создания процессов и запуска программ очень важно. В одной из следующих глав речь пойдет о командных оболочках и вызываемых из них командах.Так вот, большинство команд запускается именно таким образом, как описано выше. Что влечет за собой то, что запущенная программа наследует свойства (т.н. программное окружение) не той командной оболочки, которая выступает в качестве пользовательской, а от ее копии, которую команда заместила в рамках нового процесса. И вполне возможно, что программное окружение материнского и дочернего шеллов окажется разным, что не может не сказаться на выполнении команды.


Содержание раздела