Мир на трёх кашалотах мается
1. Процессы

Глава из книги Сага о FreeBSD

Алексей Федорчук

2008-11-19

назад | к началу

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

Каждый пользовательский процесс порождается каким-либо другим процессом — в конечном счете, в первооснове их всех лежит процесс init. Как нетрудно догадаться, порождающий процесс называется также родительским, или материнским (parent), а порожденный — дочерним (child). И идентификатор родительского процесса (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.

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

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

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

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

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

% ps l | grep v1

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


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

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

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

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

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

Управление процессами

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

Управлять процессами можно двояко. Первый способ — это отдача прямой команды. Например, выход из редактора joe автоматически приводит к отмиранию соответствующего ему процесса, комбинация клавиш Control+Z в большинстве случаев переводит процесс в режим фонового выполнения, и так далее.

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

Посылка сигналов осуществляется командой kill. Она требует указания имени сигнала (или соответствующего ему численного значения) и номера процесса, на который нужно оказать воздействие. Так, безнадежно зависший процесс можно попробовать убить следующим образом:

% kill -15 ###

где ### — номер (PID) убиваемого процесса, а 15 — численное значение для сигнала TERM (от terminated). Соответственно, команда эта может быть дана и в форме

% kill -TERM ###

По получении сигнала TERM программа, ассоциированная с процессом ###, пытается по возможности корректно завершить свою работу с записью несохраненных данных.

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


% kill -9 ###

или

% kill -KILL ###

Сигнал KILL не может быть проигнорирован никаким процессом — его получение знаменуется немедленным и неизбежным завершением работы зависшей программы. Разумеется, ни о каком сохранении данных при этом речи идти не может, поэтому применение этого сигнала — крайняя мера.

Есть и еще один способ управления процессами — изменение их приоритета с целью перераспределения ресурсов машины. Однако на практике пользователю с этим почти не приходится сталкиваться, и потому эту тему я затрону лишь вкратце.

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

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

Делается это двояко. Если требуется запустить программу с приоритетом, отличным от обычного, используется команда nice с величиной изменения nice value в качестве опции (предваряемой дефисом) и именем программы в качестве опции. Так, команда

% nice -5 joe

запустит редактор joe с приоритетом, уменьшенным на пять единиц. Если же опустить опцию, приоритет уменьшится на десять единиц. Для увеличения приоритета администратор (и только он) может дать команду

% nice --7 joe

что приведет к росту приоритета (то есть уменьшению "дружелюбия") на семь единиц.

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

% renice 7 735

данная от лица пользователя — владельца процесса с PID 735, приведет к установке для него nice value, равного 7 (при условии, что прежний приоритет этого процесса был выше, например, 3 или 0). Администратор же может понизить приоритет того же процесса до значения nice value, равного 2, с помощью команды

# renice 2 735

или присвоить ему максимальный приоритет:

# renice -20 735

В завершение разговора о процессах повторю, что пользователь может управлять только теми процессами, хозяином которых он является — то есть собственноручно запущенными интерактивными. Администратор системы же располагает правами на управление всеми запущенными процессами, в том числе и неинтерактивными демонами.

назад | к началу