Чтение онлайн

ЖАНРЫ

Шрифт:

Реальный (RGID) и эффективный (EGID) идентификаторы группы

Реальный идентификатор группы равен идентификатору первичной или текущей группы пользователя, запустившего процесс. Эффективный идентификатор служит для определения прав доступа к системным ресурсам по классу доступа группы. Так же как и для эффективного идентификатора пользователя, возможна его установка равным идентификатору группы владельца исполняемого файла (флаг SGID).

Команда ps(1) (process status) позволяет вывести список процессов, выполняющихся в системе, и их атрибуты:

$ ps -ef | head -20

UID PID PPID С STIME TTY TIME CMD

root 0 0 0 Dec 17 ? 0:00 sched

root 1 0 0 Dec 17 ? 0:01 /etc/init -

root 2 0 0 Dec 17 ? 0:00 pageout

root 3 0 0 Dec 17 ? 7:00 fsflush

root 164 1 0 Dec 17 ? 0:01 /usr/lib/sendmail -bd -q1h

fed 627 311 0 Dec 17 pts/3 0:27 emiclock

fed 314 304 0 Dec 17 pts/4 0:00 /usr/local/bin/bash

fed 3521 512 0 0:01 <defunct>

Более

подробное описание полей вывода команды ps(1) приведено далее в разделе "Основные утилиты UNIX".

Жизненный путь процесса

Процесс в UNIX создается системным вызовом fork(2). Процесс, сделавший вызов fork(2) называется родительским, а вновь созданный процесс — дочерним. Новый процесс является точной копией породившего его процесса. Как это ни удивительно, но новый процесс имеет те же инструкции и данные, что и его родитель. Более того, выполнение родительского и дочернего процесса начнется с одной и той же инструкции, следующей за вызовом fork(2). Единственно, чем они различаются — это идентификатором процесса PID. Каждый процесс имеет одного родителя, но может иметь несколько дочерних процессов.

Для запуска задачи, т.е. для загрузки новой программы, процесс должен выполнить системный вызов exec(2). При этом новый процесс не порождается, а исполняемый код процесса полностью замещается кодом запускаемой программы. Тем не менее окружение новой программы во многом сохраняется, в частности сохраняются значения переменных окружения, назначения стандартных потоков ввода/вывода, вывода сообщений об ошибках, а также приоритет процесса.

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

Рассмотрим эту схему на примере.

Допустим, пользователь, работая в командном режиме (в командном интерпретаторе shell) запускает команду ls(1). Текущий процесс (shell) делает вызов fork(2), порождая вторую копию shell. В свою очередь, порожденный shell вызывает exec(2), указывая в качестве параметра имя исполняемого файла, образ которого необходимо загрузить в память вместо кода shell. Код ls(1) замещает код порожденного shell, и утилита ls(1) начинает выполняться. По завершении работы ls(1) созданный процесс "умирает". Пользователь вновь возвращается в командный режим. Описанный процесс представлен на рис. 1.5. Мы также проиллюстрируем работу командного интерпретатора в примере, приведенном в главе 2.

Рис. 1.5. Создание процесса и запуск программы

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

Пользователь работает в командном режиме:

UID PID PPID С STIME TTY TIME CMD

user1 745 1 10 10:11:34 ttyp4 0:01 sh

Пользователь запустил команду ls(1), и shell произвел вызов fork(2):

UID PID PPID С STIME TTY TIME CMD

user1 745 1 10 10:11:34 ttyp4 0:01 sh

user1 802 745 14 11:00:00 ttyp4 0:00 sh

Порожденный shell

произвел вызов exec(2):

UID PID PPID С STIME TTY TIME CMD

user1 745 1 10 10:11:34 ttyp4 0:01 sh

user1 802 745 12 11:00:00 ttyp4 0:00 ls

Процесс ls(1) завершил работу:

UID PID PPID С STIME TTY TIME CMD

user1 745 1 10 10:11:34 ttyp4 0:01 sh

Описанная процедура запуска новой программы называется fork-and-exec.

Однако бывают ситуации, когда достаточно одного вызова fork(2) без последующего exec(2). В этом случае исполняемый код родительского процесса должен содержать логическое ветвление для родительского и дочернего процессов [9] .

9

Такое ветвление можно организовать на основании значения, возвращаемого системным вызовом fork(2). Для родительского процесса fork возвращает идентификатор созданного дочернего процесса, а дочерний процесс получает значение, равное 0. Подробнее эти вопросы будут рассмотрены в главе 2.

Все процессы в UNIX создаются посредством вызова fork(2). Запуск на выполнение новых задач осуществляется либо по схеме fork-and-exec, либо с помощью exec(2). "Прародителем" всех процессов является процесс init(1М), называемый также распределителем процессов. Если построить граф "родственных отношений" между процессами, то получится дерево, корнем которого является init(1M). Показанные на рис. 1.6 процессы sched и vhand являются системными и формально не входят в иерархию (они будут рассматриваться в следующих главах).

Рис. 1.6. Типичное "дерево" процессов в UNIX

Сигналы

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

SIGFPE
, а при нажатии клавиш прерывания, обычно <Del> или <Ctrl>+<C>, текущему процессу посылается сигнал
SIGINT
.

Для отправления сигнала служит команда kill(1):

kill sig_no pid

где

sig_nо
— номер или символическое название сигнала, a
pid
— идентификатор процесса, которому посылается сигнал. Администратор системы может посылать сигналы любым процессам, обычный же пользователь может посылать сигналы только процессам, владельцем которых он является (реальный и эффективный идентификаторы процесса должны совпадать с идентификатором пользователя [10] ). Например, чтобы послать процессу, который вы только что запустили в фоновом режиме, сигнал завершения выполнения
SIGTERM
, можно воспользоваться командой:

10

Точнее, с реальным и эффективным идентификаторами процесса, посылающего сигнал. Если вы посылаете сигнал командой kill(1), работая в shell, то речь идет о командном интерпретаторе.

Поделиться с друзьями: