Напомним данное в стандарте POSIX-2001 определение процесса.
Каждый процесс обладает целым рядом атрибутов. Важнейшим среди
них является
Процессы могут создаваться и завершаться.
После того как процесс создан с помощью функции fork(), он
считается активным. До завершения процесса в его рамках существуют по
крайней мере один поток управления и адресное пространство.
Процесс может перейти в неактивное состояние, и тогда некоторые
из его ресурсов (но не идентификатор) могут быть возвращены системе.
Когда по отношению к неактивному процессу выполняют функцию
семейства wait(), системе возвращаются остальные ресурсы. Последний
из них – идентификатор процесса, и на этом время жизни процесса
заканчивается.
Завершение процесса может быть нормальным или аварийным.
Нормальное завершение происходит, в частности, при возврате из функции main().
Задания, группы процессов и процессы подразделяются на
В момент создания процесса пользователь входил в некоторую
группу, идентификатор которой называется
Для определения прав процесса (в том числе прав доступа к файлам)
применяются
Поведение процесса определяется исполняемой в его рамках
программой – последовательностью инструкций для решения определенной
задачи. Программы хранятся в файлах. В
Стандартом POSIX-2001 предусмотрены также сохраненные
переустановленные действующие идентификаторы пользователя (
При определении прав доступа к файлам наравне с действующим
С процессом ассоциируются
Текущий и корневой каталоги и набор дескрипторов открытых
файлов также относятся к числу
Все перечисленные выше атрибуты разделяются существующими в
рамках процесса
Всем потокам управления одного процесса доступны все объекты,
адреса которых могут быть определены потоком. В число таких объектов
входят статические переменные, полученные от функции malloc()
области динамической памяти, унаследованная от системно-зависимых
функций прямоадресуемая память,
По отношению к потокам управления вводится понятие безопасных функций, которые можно вызывать параллельно в нескольких потоках без нарушения корректности их функционирования. К числу безопасных принадлежат "чистые" функции, а также функции, обеспечивающие взаимное исключение перед доступом к разделяемым объектам. Если в стандарте явно не оговорено противное, функция считается потоковобезопасной.
PTHREAD_SCOPE_SYSTEM и PTHREAD_SCOPE_PROCESS.
Данные выше определения будут поясняться и раскрываться по мере описания служебных программ и функций, обслуживающих понятие процесса.
Для выдачи информации о процессах служит утилита ps:
ps [-aA] [-defl] [-G список_групп] [-o формат] ... [-p список_процессов] [-t список_терминалов] [-U список_пользователей] -g список_групп] [-n список_имен] [-u список_пользователей]
По умолчанию информация выдается обо всех процессах, имеющих
тот же ps может выглядеть так, как
показано в листинге 7.1.
PID TTY TIME CMD 1594 ttyS4 00:00:02 sh 1645 ttyS4 00:00:00 sh 1654 ttyS4 00:02:45 rk.20.01 18356 ttyS4 00:00:00 prconecm 18357 ttyS4 00:00:00 sh 18358 ttyS4 00:00:00 ps
Если нужна более подробная информация о более широком наборе процессов, следует пользоваться опциями. Перечислим наиболее употребительные из них.
-a
Выдать информацию обо всех процессах, ассоциированных с
терминалами. Заметим, однако, что, во-первых, при получении информации о
процессах контролируются права доступа (например, пользователю будут
видны только порожденные им процессы), а во-вторых, по стандарту
реализация может не включать в выдаваемый список
-A
Выдать информацию обо всех процессах.
-G список_групп
Выдать информацию о процессах с заданными реальными идентификаторами групп.
-o формат
Выдать информацию о процессах в заданном формате.
-p список_процессов
Выдать информацию о процессах с заданными идентификаторами.
-t список_терминалов
Выдать информацию о процессах, ассоциированных с заданными
терминалами. Способ задания терминалов зависит от реализации.
Обычно указывается имя специального файла, например, ttyS4, или, если
имя начинается с tty, просто S4.
-U список_пользователей
Выдать информацию о процессах с заданными реальными идентификаторами пользователей (они могут указываться и в виде входных имен).
Все перечисленные опции, кроме -o, ведают отбором процессов.
Если задан целый ряд подобных опций, выводится информация обо всех
специфицированных ими процессах.
Опции -o (их в командной строке может быть несколько)
позволяют задать выходной формат информации о процессах. Указываются
выводимые поля и, если нужно, отличные от подразумеваемых тексты
соответствующих им заголовков, отделяющиеся от имени поля знаком
равенства и продолжающиеся до конца -o.
Перечислим имена полей, которые могут указываться в выходном формате, и соответствующие им подразумеваемые заголовки.
ruser (RUSER)
Выдавать
user (USER)
rgroup (RGROUP)
group (GROUP)
pid (PID)
Идентификатор процесса.
pgid (PGID)
pcpu (%CPU)
Процент процессорного времени, потребляемый процессом.
vsz (VSZ)
Размер процесса в (виртуальной) памяти (в килобайтных блоках).
nice (NI)
Число, используемое как рекомендация системе при планировании процессов. Меньшие значения соответствуют более приоритетным процессам.
etime (ELAPSED)
Астрономическое время, прошедшее с момента запуска процесса.
time (TIME)
Процессорное время, потребленное процессом.
tty (TT)
Имя
comm (COMMAND)
Имя выполняемой команды ( argv [0] ).
args (COMMAND)
Выполняемая командная строка.
Например, командная строка, показанная в листинге 7.2, породит выдачу, фрагмент которой приведен в листинге 7.3
ps -A -o ruser,user,pid,ppid,tty=TTY -o nice,vsz,args
RUSER USER PID PPID TTY NI VSZ COMMAND root root 1 0 ? 0 1372 init [5] root root 4 1 ? 19 0 [ksoftirqd_CPU0] root root 555 1 ? 0 1428 syslogd -m 0 root root 560 1 ? 0 1364 klogd -x rpc rpc 580 1 ? 0 1508 portmap rpcuser rpcuser 608 1 ? 0 1560 rpc.statd root root 743 1 ? 0 2620 /usr/sbin/sshd root root 776 1 ? 0 2200 xinetd -stayalive -reuse -pidfi root root 805 1 ? 0 1500 rpc.rquotad root root 810 1 ? 0 1504 rpc.mountd root root 897 1 ? 0 3236 /usr/libexec/postfix/master postfix postfix 906 897 ? 0 3384 nqmgr -l -n qmgr -t fifo -u -c root root 918 1 ? 0 1400 gpm -t ps/2 -m /dev/mouse root root 936 1 ? 0 1548 crond xfs xfs 968 1 ? 0 4432 xfs -droppriv -daemon nobody nobody 987 1 ? 0 36488 dictd 1.9.7: 0/0 root daemon 1022 1 ? 0 1404 /usr/sbin/atd root root 1057 1 ? 0 5768 cupsd root root 1064 1 tty1 0 1344 /sbin/mingetty tty1 root root 1070 1 ttyS2 0 1352 /sbin/agetty -i -L ttyS2 38400 root root 1072 1 ? 0 2300 login -- galat galat galat 1086 1072 ttyS4 0 2260 -sh root root 1124 1085 ? 0 16900 /usr/bin/kdm_greet postfix postfix 1826 897 ? 0 3304 pickup -l -t fifo -u -c galat galat 2013 1171 ttyS4 0 1940 /bin/sh -c ps -A -o user,pid,pp galat galat 2014 2013 ttyS4 0 2584 ps -A -o user,pid,ppid,tty=TTY
Для опроса идентификаторов процесса, родительского процесса и
группы процессов предусмотрены функции и getpgrp() (см. листинг 7.4).
#include <unistd.h> pid_t getpid (void); #include <unistd.h> pid_t getppid (void); #include <unistd.h> pid_t getpgrp (void);
По стандарту эти функции всегда завершаются успешно, поэтому ошибочных кодов возврата не предусмотрено.
Для установки setpgid() (см. листинг 7.5).
#include <unistd.h> int setpgid (pid_t pid, pid_t pgid);
Выполнение функции setpgid() влечет либо присоединение к
существующей группе процессов, либо создание новой группы в рамках
сеанса, в который входит вызывающий процесс. Процесс может установить
идентификатор группы для себя или для порожденного процесса. Нельзя
изменить
В случае успешного завершения функции setpgid() (результат при
этом равен нулю) pgid для заданного аргументом pid процесса. Если значение pid
равно нулю, установка производится для вызывающего процесса. А если
значение pgid равно нулю, то в качестве идентификатора группы
процессов используется идентификатор процесса, заданного аргументом pid.
Для создания сеанса и установки setsid() (см. листинг 7.6).
#include <unistd.h> pid_t setsid (void);
Если вызывающий процесс не является лидером группы, в
результате выполнения функции setsid() будет создан новый сеанс, причем
вызывающий процесс станет лидером этого сеанса, равно как и лидером
новой группы процессов (без
Программа, показанная в листинге 7.7, служит примером использования (в том числе некорректного) описанных функций. Возможный результат работы этой программы приведен в листинге 7.8.
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main (void) {
pid_t ppid;
pid_t pgid;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
printf ("Атрибуты текущего процесса: pid: %d,
ppid: %d, pgid: %d\n",
getpid (), ppid = getppid (), pgid =
getpgrp ());
/* Выделимся в новую группу */
if (setpgid (0, 0) != 0) {
perror ("setpgid (0, 0)");
}
printf ("Новая группа текущего процесса: %d\n",
getpgrp ());
/* Попробуем создать новый сеанс */
if (setsid () == (pid_t) (-1)) {
perror ("setsid от имени лидера группы");
}
/* Вернемся в прежнюю группу */
if (setpgid (0, pgid) != 0) {
perror ("setpgid (0, pgid)");
}
printf ("Группа текущего процесса после повторной
смены: %d\n", getpgrp ());
/* Повторим попытку создать новый сеанс */
if (setsid () == (pid_t) (-1)) {
perror ("setsid от имени не-лидера группы");
}
printf ("Группа текущего процесса после создания
нового сеанса: %d\n", getpgrp ());
/* Попробуем сменить группу родительского
процесса */
if (setpgid (ppid, 0) != 0) {
perror ("setpgid (ppid, 0)");
}
/* Попробуем установить несуществующий */
/* идентификатор группы процессов */
if (setpgid (0, ppid) != 0) {
perror ("setpgid (0, ppid)");
}
return (0);
}
Атрибуты текущего процесса: pid: 11726, ppid: 11725, pgid: 1153 Новая группа текущего процесса: 11726 setsid от имени лидера группы: Operation not permitted Группа текущего процесса после повторной смены: 1153 Группа текущего процесса после создания нового сеанса: 11726 setpgid (ppid, 0): No such process setpgid (0, ppid): Operation not permitted
Опрос реальных и getuid(), geteuid(), getgid(), getegid() (см. листинг 7.9). Как и , они всегда завершаются успешно.
#include <unistd.h> uid_t getuid (void); #include <unistd.h> uid_t geteuid (void); #include <unistd.h> gid_t getgid (void); #include <unistd.h> gid_t getegid (void);
Более сложный интерфейс имеет функция getgroups(),
предназначенная для получения идентификаторов grouplist.
#include <unistd.h>
int getgroups (int gidsetsize,
gid_t grouplist []);
Аргумент gidsetsize задает grouplist,
а реальное количество записанных идентификаторов групп возвращается в
виде результата функции. Если в качестве значения gidsetsize задать
нуль, getgroups() выдаст количество grouplist.
Переустановить и seteuid() (см. листинг
7.11). Операция разрешена, если реальный или сохраненный uid. Помимо
этого, обладающие соответствующими привилегиями процессы с помощью
функции могут установить по значению uid все три
#include <unistd.h> int setuid (uid_t uid); #include <unistd.h> int seteuid (uid_t uid);
Для непривилегированных процессов по соображениям
мобильности рекомендуется использование функции seteuid().
Аналогичные функции для переустановки идентификаторов группы процесса показаны в листинге 7.12.
#include <unistd.h> int setgid (gid_t gid); #include <unistd.h> int setegid (gid_t gid);
Отметим, что функция для изменения списка дополнительных
групп setgroups() относится к числу привилегированных и,
следовательно, остается за рамками стандарта POSIX.
Проиллюстрируем использование описанных функций с помощью программы, показанной в листинге 7.13.
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main (void) {
uid_t uid;
int nsupgroups;
gid_t *supgroupslist;
int i;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
printf ("Идентификаторы пользователя текущего
процесса:\n" " реальный: %d, действующий:
%d\n", uid = getuid (), geteuid ());
printf ("Идентификаторы группы текущего
процесса:\n" " реальный: %d, действующий:
%d\n", getgid (), getegid ());
printf ("Количество дополнительных групп
текущего процесса: %d\n",
nsupgroups = getgroups (0, supgroupslist));
if (nsupgroups > 0) {
if ((supgroupslist = (gid_t *) malloc
(nsupgroups * sizeof (gid_t))) == NULL) {
perror ("MALLOC");
} else if (getgroups (nsupgroups,
supgroupslist) == (-1)) {
perror ("GETGROUPS");
} else {
/* Выдадим идентификаторы дополнительных */
/* групп процесса */
printf ("Идентификаторы дополнительных групп
текущего процесса:\n");
for (i = 0; i < nsupgroups; i++) {
printf (" %d", supgroupslist [i]);
}
printf ("\n");
}
}
/* Попробуем переустановить идентификатор */
/* пользователя процесса */
if (setuid ((uid_t) 1) != 0) {
perror ("setuid (1)");
}
printf ("Идентификаторы пользователя текущего
процесса после первой смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
/* Попробуем вернуть прежний идентификатор */
/* пользователя процесса */
if (setuid (uid) != 0) {
perror ("setuid (uid)");
}
printf ("Идентификаторы пользователя текущего
процесса после второй смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
/* Попробуем сменить действующий идентификатор */
/* с помощью функции seteuid() */
if (seteuid ((uid_t) 1) != 0) {
perror ("seteuid (1)");
}
printf ("Идентификаторы пользователя текущего
процесса после третьей смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
return (0);
}
Если эту программу запустить от имени обычного пользователя, результат может выглядеть так, как показано в листинге 7.14.
Идентификаторы пользователя текущего процесса: реальный: 108, действующий: 108 Идентификаторы группы текущего процесса: реальный: 3, действующий: 3 Количество дополнительных групп текущего процесса: 1 Идентификаторы дополнительных групп текущего процесса: 3 setuid (1): Operation not permitted Идентификаторы пользователя текущего процесса после первой смены: реальный: 108, действующий: 108 Идентификаторы пользователя текущего процесса после второй смены: реальный: 108, действующий: 108 seteuid (1): Operation not permitted Идентификаторы пользователя текущего процесса после третьей смены: реальный: 108, действующий: 108
После запуска той же программы от имени суперпользователя может получиться результат, показанный в листинге 7.15.
Идентификаторы пользователя текущего процесса: реальный: 0, действующий: 0 Идентификаторы группы текущего процесса: реальный: 0, действующий: 0 Количество дополнительных групп текущего процесса: 7 Идентификаторы дополнительных групп текущего процесса: 0 1 2 3 4 6 10 Идентификаторы пользователя текущего процесса после первой смены: реальный: 1, действующий: 1 setuid (uid): Operation not permitted Идентификаторы пользователя текущего процесса после второй смены: реальный: 1, действующий: 1 Идентификаторы пользователя текущего процесса после третьей смены: реальный: 1, действующий: 1
Утерять статус суперпользователя легко, а вернуть трудно...
Наконец, сделаем владельцем выполнимого файла рассматриваемой
программы пользователя с идентификатором 1, то же проделаем с
владеющей группой, взведем в режиме этого файла chmod ug+s ) и вновь
запустим его от имени обычного пользователя (см. листинг 7.16).
Идентификаторы пользователя текущего процесса: реальный: 108, действующий: 1 Идентификаторы группы текущего процесса: реальный: 3, действующий: 1 Количество дополнительных групп текущего процесса: 1 Идентификаторы дополнительных групп текущего процесса: 3 Идентификаторы пользователя текущего процесса после первой смены: реальный: 108, действующий: 1 Идентификаторы пользователя текущего процесса после второй смены: реальный: 108, действующий: 108 Идентификаторы пользователя текущего процесса после третьей смены: реальный: 108, действующий: 1
Видно, что сохраненный
Отметим, что если функция getgroups() применяется в
программе несколько раз, то память под массив supgroupslist лучше отвести
по максимуму, воспользовавшись выражением вида nsupgroups =
sysconf (_SC_NGROUPS_MAX) + 1;.
Для опроса и/или изменения umask:
umask [-S] [маска]
и одноименная функция (см. листинг 7.17).
#include <sys/stat.h>> mode_t umask (mode_t cmask);
Согласно стандарту, в маске могут фигурировать только биты режима доступа к файлам; трактовка прочих бит зависит от реализации.
Для служебной программы umask маска может задаваться и
выводиться в символьной и восьмеричной формах. Маска в символьной форме
является "позитивной" в том смысле, что заданные в ней umask аналогичен
утилите chmod.
Опция -S предписывает выдавать маску в символьной форме.
Функция umask() устанавливает новую ("негативную") маску и
возвращает старую.
Пример. Последовательность команд, показанная в листинге 7.18, выдаст результат, воспроизведенный в листинге 7.19.
umask 0 umask -S umask -- -x umask
u=rwx,g=rwx,o=rwx 0111
Обратим внимание на употребление признака конца опций --. Он
позволяет задавать аргументы, начинающиеся с минуса. Отметим также,
что результаты, выдаваемые umask, могут в дальнейшем
использоваться как аргументы данной утилиты (и в символьной, и в восьмеричных
формах).
Новые процессы создаются при помощи функции fork() (см. листинг 7.20).
#include <unistd.h> pid_t fork (void);
Новый (порожденный) процесс является точной копией процесса,
вызвавшего fork() (родительского), за исключением следующих
моментов.
fork().В случае успешного завершения функция fork() возвращает
порожденному процессу 0, а родительскому процессу – идентификатор
порожденного процесса. После этого оба процесса начинают
независимо выполнять инструкции, расположенные за обращением к fork().
При неудаче родительскому процессу возвращается -1, новый процесс
не создается.
Поскольку возвращаемые функцией fork() значения различны
для обеих копий, родительский и порожденный процессы могут далее
выполняться по-разному. Например, процесс-предок переходит в
состояние ожидания завершения процесса-потомка либо, если
процесс-потомок запущен асинхронно, продолжает выполнение параллельно с ним.
Процесс-потомок при помощи функции семейства exec() подменяет
программу, которая определяет поведение процесса, и передает ей
управление и список аргументов.
Напомним, что заголовок функции main() C-программы выглядит
в общем случае так, как показано в листинге 7.21.
int main (int argc, char *argv []);
Значение argc равно количеству аргументов; argv – это массив
указателей собственно на аргументы, которые определяются исходя из
командной строки, запускающей C-программу. В соответствии с
принятым соглашением, значение argc не меньше единицы, а первый элемент
массива argv указывает на цепочку символов, содержащую имя
выполняемого файла.
Аналогичный смысл имеют аргументы функций семейства exec()
(см. листинг 7.22).
#include <unistd.h> extern char **environ; int execl (const char *path, const char *arg0, ... /*, (char *) 0 */); int execv (const char *path, char *const argv []); int execle (const char *path, const char *arg0, ... /*, (char *) 0, char *const envp [] */); int execve (const char *path, char *const argv [], char *const envp []); int execlp (const char *file, const char *arg0, ... /*, (char *) 0 */); int execvp (const char *file, char *const argv []);
Функции семейства exec() заменяют текущий образ процесса
новым (и, следовательно, в случае успешного завершения возврат в
вызывающий процесс невозможен). Новый образ создается на основе выполнимого
файла, называемого файлом образа процесса.
Переменная environ инициализируется как указатель на массив
указателей на составляющие окружение цепочки символов. Массивы argv и environ завершаются пустым указателем.
Аргумент path указывает на
Аргумент file имеет аналогичный смысл, однако, если он задан как
простое имя, то производится поиск в каталогах, заданных переменной
окружения PATH.
Аргументы arg0, ..., являются указателями на цепочки символов,
составляющие список аргументов нового образа процесса. Последним в
списке располагается пустой указатель, а аргумент arg0 должен
указывать на имя файла-образа.
Аргумент envp имеет тот же смысл и назначение, что и переменная environ.
Файловые дескрипторы остаются открытыми в новом образе, если
только они не были снабжены флагом FD_CLOEXEC.
Если у файла с новым образом процесса взведен
Следующие
Родительский процесс реализует ожидание завершения
процессов-потомков и получает информацию о его (завершения) статусе с
помощью функций семейства wait() (см. листинг 7.23). Если
информация о статусе завершения была доступна до вызова wait(),
родительский процесс не приостанавливается, возврат из wait() происходит
немедленно.
#include <sys/wait.h> pid_t wait (int *stat_loc); pid_t waitpid (pid_t pid, int *stat_loc, int options);
Функция waitpid() эквивалентна wait(), если аргумент pid
равен (pid_t) (-1), а аргумент options имеет нулевое значение.
Аргумент pid задает набор порожденных процессов, (pid_t) (-1) представляет
произвольный элемент множества порожденных процессов. Если pid > 0>,
имеется в виду один процесс с данным идентификатором. Нулевое значение
специфицирует любого потомка из той же группы процессов, что и
вызывающий. Наконец, при pid < (pid_t) (-1) запрашивается статус
завершения любого порожденного процесса из группы, идентификатор
которой равен абсолютной величине pid.
Аргумент options задается как побитное ИЛИ следующих флагов,
определенных в заголовочном файле <sys/wait.h>.
WNOHANG
Функция waitpid() не приостанавливает выполнение вызывающего
потока управления, если запрашиваемый статус не доступен немедленно.
WUNTRACED
Наряду со статусом завершения запрашивать статус остановленных, но еще не опрошенных процессов.
Если запрос статуса порожденного процесса завершился успешно,
функции wait() и waitpid() возвращают идентификатор этого
процесса и размещают по указателю stat_loc (если он отличен от NULL )
значение, которое будет нулевым тогда и только тогда, когда выдан статус
порожденного процесса, завершившегося по одной из трех причин:
main с нулевым результатом;_exit() или exit() с
нулевым аргументом;Для анализа целочисленного значения stat_val, на которое
указывает аргумент stat_loc, в файле <sys/wait.h> определен набор макросов.
Например, значение WIFEXITED (stat_val ) будет ненулевым в случае
нормального завершения порожденного процесса; при этом WEXITSTATUS
(stat_val) возвращает младший байт статуса. Макрос WIFSTOPPED
(stat_val) возвращает ненулевое значение, если получен статус остановленного процесса.
Процесс может вызвать собственное завершение, обратившись к
функциям семейства exit() (см. листинг 7.24).
#include <stdlib.h> void exit (int status); void _Exit (int status); #include <unistd.h> void _exit (int status);
Значением аргумента status могут служить константы 0, EXIT_SUCCESS, EXIT_FAILURE или любое другое значение, хотя ожидающему
родительскому процессу будет доступен только младший байт статуса ( status 0377 ).
Функция exit(), в отличие от _Exit() и _exit(), производит
аккуратное завершение: выполняет зарегистрированные функции (см. ниже
описание функции atexit() ), выталкивает буфера, закрывает потоки,
удаляет временные файлы и т.д. Функции _Exit() и _exit()
эквивалентны. Все функции семейства exit() закрывают файловые
дескрипторы, открытые вызывающим процессом.
Если родительский процесс выполняет функции wait() или waitpid(), ему передается младший байт значения status. Если
родительский процесс этого не делает, функции семейства exit() переводят
вызывающий процесс в состояние зомби.
Если у завершающегося процесса были потомки, родительским для них становится системный процесс, определяемый реализацией.
Функция atexit() (см. листинг 7.25) позволяет зарегистрировать
функции, которые будут вызываться, если процесс завершается,
обращаясь к exit() или возвращаясь из main().
#include <stdlib.h> int atexit (void (*func) (void));
Реализация должна поддерживать регистрацию по крайней мере тридцати двух функций и вызывать их в обратном порядке.
В листинге 7.26 приведен пример использования функций порождения и завершения процессов.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
static void atefunc (void) {
/* Перед завершением выдадим информацию о */
/* процессах */
printf ("Ситуация перед завершением
родительского процесса\n");
(void) system ("ps -o pid,ppid,vsz,args");
}
int main (void) {
int pid;
int stat;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
/* Зарегистрируем обработчик завершения процесса */
if (atexit (atefunc) != 0) {
perror ("ATEXIT");
exit (1);
}
/* Создадим новый процесс */
if ((pid = fork ()) < 0) {
perror ("FORK");
exit (2);
} else if (pid == 0) {
/* Порожденный процесс */
/* Выполним служебную программу ps */
printf ("Ситуация с точки зрения порожденного
процесса\n");
(void) execl ("/bin/ps", "ps", "-o",
"pid,ppid,args", (char *) 0);
perror ("EXEC");
exit (3); /* execl() завершился неудачей */
} else {
/* Родительский процесс */
sleep (1);
/* Вероятно, порожденный процесс уже */
/* завершился */
/* Посмотрим на текущую ситуацию */
printf ("Ситуация перед вызовом waitpid() в
родительском процессе\n");
(void) system ("ps -o pid,ppid,vsz,args");
(void) waitpid (pid, stat, 0);
printf ("Статус завершения порожденного
процесса с идентификатором %d: %d\n", pid, stat);
}
return 0;
}
Результат работы этой программы может выглядеть так,
как показано в листинге 7.27 (выполнимый файл программы имеет имя prog30 ).
Ситуация с точки зрения порожденного процесса PID PPID COMMAND 6123 1072 -sh 29568 6123 prog30 29569 29568 ps -o pid,ppid,args Ситуация перед вызовом waitpid() в родительском процессе PID PPID VSZ COMMAND 6123 1072 2260 -sh 29568 6123 1340 prog30 29569 29568 0 [ps <defunct>] 29570 29568 2584 ps -o pid,ppid,vsz,args Статус завершения порожденного процесса с идентификатором 29569: 0 Ситуация перед завершением родительского процесса PID PPID VSZ COMMAND 6123 1072 2260 -sh 29568 6123 1340 prog30 29571 29568 2584 ps -o pid,ppid,vsz,args
Обратим внимание на информацию о зомби-процессе (с пометкой <defunct> ), а также на то, что функция, зарегистрированная с помощью atexit(), вызвана в результате возврата из main().
Для терминирования процессов извне предназначена служебная
программа kill, вызванная следующим образом:
kill -s TERM идентификатор_процесса ...
Если это не помогло, можно применить более сильный вариант:
kill -s KILL идентификатор_процесса ...
Разумеется, попытки терминирования процессов подвержены контролю прав доступа.
Напомним данное в стандарте POSIX-2001 определение процесса.
Каждый процесс обладает целым рядом атрибутов. Важнейшим среди
них является
Процессы могут создаваться и завершаться.
После того как процесс создан с помощью функции fork(), он
считается активным. До завершения процесса в его рамках существуют по
крайней мере один поток управления и адресное пространство.
Процесс может перейти в неактивное состояние, и тогда некоторые
из его ресурсов (но не идентификатор) могут быть возвращены системе.
Когда по отношению к неактивному процессу выполняют функцию
семейства wait(), системе возвращаются остальные ресурсы. Последний
из них – идентификатор процесса, и на этом время жизни процесса
заканчивается.
Завершение процесса может быть нормальным или аварийным.
Нормальное завершение происходит, в частности, при возврате из функции main().
Задания, группы процессов и процессы подразделяются на
В момент создания процесса пользователь входил в некоторую
группу, идентификатор которой называется
Для определения прав процесса (в том числе прав доступа к файлам)
применяются
Поведение процесса определяется исполняемой в его рамках
программой – последовательностью инструкций для решения определенной
задачи. Программы хранятся в файлах. В
Стандартом POSIX-2001 предусмотрены также сохраненные
переустановленные действующие идентификаторы пользователя (
При определении прав доступа к файлам наравне с действующим
С процессом ассоциируются
Текущий и корневой каталоги и набор дескрипторов открытых
файлов также относятся к числу
Все перечисленные выше атрибуты разделяются существующими в
рамках процесса
Всем потокам управления одного процесса доступны все объекты,
адреса которых могут быть определены потоком. В число таких объектов
входят статические переменные, полученные от функции malloc()
области динамической памяти, унаследованная от системно-зависимых
функций прямоадресуемая память,
По отношению к потокам управления вводится понятие безопасных функций, которые можно вызывать параллельно в нескольких потоках без нарушения корректности их функционирования. К числу безопасных принадлежат "чистые" функции, а также функции, обеспечивающие взаимное исключение перед доступом к разделяемым объектам. Если в стандарте явно не оговорено противное, функция считается потоковобезопасной.
PTHREAD_SCOPE_SYSTEM и PTHREAD_SCOPE_PROCESS.
Данные выше определения будут поясняться и раскрываться по мере описания служебных программ и функций, обслуживающих понятие процесса.
Для выдачи информации о процессах служит утилита ps:
ps [-aA] [-defl] [-G список_групп] [-o формат] ... [-p список_процессов] [-t список_терминалов] [-U список_пользователей] -g список_групп] [-n список_имен] [-u список_пользователей]
По умолчанию информация выдается обо всех процессах, имеющих
тот же ps может выглядеть так, как
показано в листинге 7.1.
PID TTY TIME CMD 1594 ttyS4 00:00:02 sh 1645 ttyS4 00:00:00 sh 1654 ttyS4 00:02:45 rk.20.01 18356 ttyS4 00:00:00 prconecm 18357 ttyS4 00:00:00 sh 18358 ttyS4 00:00:00 ps
Если нужна более подробная информация о более широком наборе процессов, следует пользоваться опциями. Перечислим наиболее употребительные из них.
-a
Выдать информацию обо всех процессах, ассоциированных с
терминалами. Заметим, однако, что, во-первых, при получении информации о
процессах контролируются права доступа (например, пользователю будут
видны только порожденные им процессы), а во-вторых, по стандарту
реализация может не включать в выдаваемый список
-A
Выдать информацию обо всех процессах.
-G список_групп
Выдать информацию о процессах с заданными реальными идентификаторами групп.
-o формат
Выдать информацию о процессах в заданном формате.
-p список_процессов
Выдать информацию о процессах с заданными идентификаторами.
-t список_терминалов
Выдать информацию о процессах, ассоциированных с заданными
терминалами. Способ задания терминалов зависит от реализации.
Обычно указывается имя специального файла, например, ttyS4, или, если
имя начинается с tty, просто S4.
-U список_пользователей
Выдать информацию о процессах с заданными реальными идентификаторами пользователей (они могут указываться и в виде входных имен).
Все перечисленные опции, кроме -o, ведают отбором процессов.
Если задан целый ряд подобных опций, выводится информация обо всех
специфицированных ими процессах.
Опции -o (их в командной строке может быть несколько)
позволяют задать выходной формат информации о процессах. Указываются
выводимые поля и, если нужно, отличные от подразумеваемых тексты
соответствующих им заголовков, отделяющиеся от имени поля знаком
равенства и продолжающиеся до конца -o.
Перечислим имена полей, которые могут указываться в выходном формате, и соответствующие им подразумеваемые заголовки.
ruser (RUSER)
Выдавать
user (USER)
rgroup (RGROUP)
group (GROUP)
pid (PID)
Идентификатор процесса.
pgid (PGID)
pcpu (%CPU)
Процент процессорного времени, потребляемый процессом.
vsz (VSZ)
Размер процесса в (виртуальной) памяти (в килобайтных блоках).
nice (NI)
Число, используемое как рекомендация системе при планировании процессов. Меньшие значения соответствуют более приоритетным процессам.
etime (ELAPSED)
Астрономическое время, прошедшее с момента запуска процесса.
time (TIME)
Процессорное время, потребленное процессом.
tty (TT)
Имя
comm (COMMAND)
Имя выполняемой команды ( argv [0] ).
args (COMMAND)
Выполняемая командная строка.
Например, командная строка, показанная в листинге 7.2, породит выдачу, фрагмент которой приведен в листинге 7.3
ps -A -o ruser,user,pid,ppid,tty=TTY -o nice,vsz,args
RUSER USER PID PPID TTY NI VSZ COMMAND root root 1 0 ? 0 1372 init [5] root root 4 1 ? 19 0 [ksoftirqd_CPU0] root root 555 1 ? 0 1428 syslogd -m 0 root root 560 1 ? 0 1364 klogd -x rpc rpc 580 1 ? 0 1508 portmap rpcuser rpcuser 608 1 ? 0 1560 rpc.statd root root 743 1 ? 0 2620 /usr/sbin/sshd root root 776 1 ? 0 2200 xinetd -stayalive -reuse -pidfi root root 805 1 ? 0 1500 rpc.rquotad root root 810 1 ? 0 1504 rpc.mountd root root 897 1 ? 0 3236 /usr/libexec/postfix/master postfix postfix 906 897 ? 0 3384 nqmgr -l -n qmgr -t fifo -u -c root root 918 1 ? 0 1400 gpm -t ps/2 -m /dev/mouse root root 936 1 ? 0 1548 crond xfs xfs 968 1 ? 0 4432 xfs -droppriv -daemon nobody nobody 987 1 ? 0 36488 dictd 1.9.7: 0/0 root daemon 1022 1 ? 0 1404 /usr/sbin/atd root root 1057 1 ? 0 5768 cupsd root root 1064 1 tty1 0 1344 /sbin/mingetty tty1 root root 1070 1 ttyS2 0 1352 /sbin/agetty -i -L ttyS2 38400 root root 1072 1 ? 0 2300 login -- galat galat galat 1086 1072 ttyS4 0 2260 -sh root root 1124 1085 ? 0 16900 /usr/bin/kdm_greet postfix postfix 1826 897 ? 0 3304 pickup -l -t fifo -u -c galat galat 2013 1171 ttyS4 0 1940 /bin/sh -c ps -A -o user,pid,pp galat galat 2014 2013 ttyS4 0 2584 ps -A -o user,pid,ppid,tty=TTY
Для опроса идентификаторов процесса, родительского процесса и
группы процессов предусмотрены функции и getpgrp() (см. листинг 7.4).
#include <unistd.h> pid_t getpid (void); #include <unistd.h> pid_t getppid (void); #include <unistd.h> pid_t getpgrp (void);
По стандарту эти функции всегда завершаются успешно, поэтому ошибочных кодов возврата не предусмотрено.
Для установки setpgid() (см. листинг 7.5).
#include <unistd.h> int setpgid (pid_t pid, pid_t pgid);
Выполнение функции setpgid() влечет либо присоединение к
существующей группе процессов, либо создание новой группы в рамках
сеанса, в который входит вызывающий процесс. Процесс может установить
идентификатор группы для себя или для порожденного процесса. Нельзя
изменить
В случае успешного завершения функции setpgid() (результат при
этом равен нулю) pgid для заданного аргументом pid процесса. Если значение pid
равно нулю, установка производится для вызывающего процесса. А если
значение pgid равно нулю, то в качестве идентификатора группы
процессов используется идентификатор процесса, заданного аргументом pid.
Для создания сеанса и установки setsid() (см. листинг 7.6).
#include <unistd.h> pid_t setsid (void);
Если вызывающий процесс не является лидером группы, в
результате выполнения функции setsid() будет создан новый сеанс, причем
вызывающий процесс станет лидером этого сеанса, равно как и лидером
новой группы процессов (без
Программа, показанная в листинге 7.7, служит примером использования (в том числе некорректного) описанных функций. Возможный результат работы этой программы приведен в листинге 7.8.
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main (void) {
pid_t ppid;
pid_t pgid;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
printf ("Атрибуты текущего процесса: pid: %d,
ppid: %d, pgid: %d\n",
getpid (), ppid = getppid (), pgid =
getpgrp ());
/* Выделимся в новую группу */
if (setpgid (0, 0) != 0) {
perror ("setpgid (0, 0)");
}
printf ("Новая группа текущего процесса: %d\n",
getpgrp ());
/* Попробуем создать новый сеанс */
if (setsid () == (pid_t) (-1)) {
perror ("setsid от имени лидера группы");
}
/* Вернемся в прежнюю группу */
if (setpgid (0, pgid) != 0) {
perror ("setpgid (0, pgid)");
}
printf ("Группа текущего процесса после повторной
смены: %d\n", getpgrp ());
/* Повторим попытку создать новый сеанс */
if (setsid () == (pid_t) (-1)) {
perror ("setsid от имени не-лидера группы");
}
printf ("Группа текущего процесса после создания
нового сеанса: %d\n", getpgrp ());
/* Попробуем сменить группу родительского
процесса */
if (setpgid (ppid, 0) != 0) {
perror ("setpgid (ppid, 0)");
}
/* Попробуем установить несуществующий */
/* идентификатор группы процессов */
if (setpgid (0, ppid) != 0) {
perror ("setpgid (0, ppid)");
}
return (0);
}
Атрибуты текущего процесса: pid: 11726, ppid: 11725, pgid: 1153 Новая группа текущего процесса: 11726 setsid от имени лидера группы: Operation not permitted Группа текущего процесса после повторной смены: 1153 Группа текущего процесса после создания нового сеанса: 11726 setpgid (ppid, 0): No such process setpgid (0, ppid): Operation not permitted
Опрос реальных и getuid(), geteuid(), getgid(), getegid() (см. листинг 7.9). Как и , они всегда завершаются успешно.
#include <unistd.h> uid_t getuid (void); #include <unistd.h> uid_t geteuid (void); #include <unistd.h> gid_t getgid (void); #include <unistd.h> gid_t getegid (void);
Более сложный интерфейс имеет функция getgroups(),
предназначенная для получения идентификаторов grouplist.
#include <unistd.h>
int getgroups (int gidsetsize,
gid_t grouplist []);
Аргумент gidsetsize задает grouplist,
а реальное количество записанных идентификаторов групп возвращается в
виде результата функции. Если в качестве значения gidsetsize задать
нуль, getgroups() выдаст количество grouplist.
Переустановить и seteuid() (см. листинг
7.11). Операция разрешена, если реальный или сохраненный uid. Помимо
этого, обладающие соответствующими привилегиями процессы с помощью
функции могут установить по значению uid все три
#include <unistd.h> int setuid (uid_t uid); #include <unistd.h> int seteuid (uid_t uid);
Для непривилегированных процессов по соображениям
мобильности рекомендуется использование функции seteuid().
Аналогичные функции для переустановки идентификаторов группы процесса показаны в листинге 7.12.
#include <unistd.h> int setgid (gid_t gid); #include <unistd.h> int setegid (gid_t gid);
Отметим, что функция для изменения списка дополнительных
групп setgroups() относится к числу привилегированных и,
следовательно, остается за рамками стандарта POSIX.
Проиллюстрируем использование описанных функций с помощью программы, показанной в листинге 7.13.
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main (void) {
uid_t uid;
int nsupgroups;
gid_t *supgroupslist;
int i;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
printf ("Идентификаторы пользователя текущего
процесса:\n" " реальный: %d, действующий:
%d\n", uid = getuid (), geteuid ());
printf ("Идентификаторы группы текущего
процесса:\n" " реальный: %d, действующий:
%d\n", getgid (), getegid ());
printf ("Количество дополнительных групп
текущего процесса: %d\n",
nsupgroups = getgroups (0, supgroupslist));
if (nsupgroups > 0) {
if ((supgroupslist = (gid_t *) malloc
(nsupgroups * sizeof (gid_t))) == NULL) {
perror ("MALLOC");
} else if (getgroups (nsupgroups,
supgroupslist) == (-1)) {
perror ("GETGROUPS");
} else {
/* Выдадим идентификаторы дополнительных */
/* групп процесса */
printf ("Идентификаторы дополнительных групп
текущего процесса:\n");
for (i = 0; i < nsupgroups; i++) {
printf (" %d", supgroupslist [i]);
}
printf ("\n");
}
}
/* Попробуем переустановить идентификатор */
/* пользователя процесса */
if (setuid ((uid_t) 1) != 0) {
perror ("setuid (1)");
}
printf ("Идентификаторы пользователя текущего
процесса после первой смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
/* Попробуем вернуть прежний идентификатор */
/* пользователя процесса */
if (setuid (uid) != 0) {
perror ("setuid (uid)");
}
printf ("Идентификаторы пользователя текущего
процесса после второй смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
/* Попробуем сменить действующий идентификатор */
/* с помощью функции seteuid() */
if (seteuid ((uid_t) 1) != 0) {
perror ("seteuid (1)");
}
printf ("Идентификаторы пользователя текущего
процесса после третьей смены:\n"
" реальный: %d, действующий: %d\n",
getuid (), geteuid ());
return (0);
}
Если эту программу запустить от имени обычного пользователя, результат может выглядеть так, как показано в листинге 7.14.
Идентификаторы пользователя текущего процесса: реальный: 108, действующий: 108 Идентификаторы группы текущего процесса: реальный: 3, действующий: 3 Количество дополнительных групп текущего процесса: 1 Идентификаторы дополнительных групп текущего процесса: 3 setuid (1): Operation not permitted Идентификаторы пользователя текущего процесса после первой смены: реальный: 108, действующий: 108 Идентификаторы пользователя текущего процесса после второй смены: реальный: 108, действующий: 108 seteuid (1): Operation not permitted Идентификаторы пользователя текущего процесса после третьей смены: реальный: 108, действующий: 108
После запуска той же программы от имени суперпользователя может получиться результат, показанный в листинге 7.15.
Идентификаторы пользователя текущего процесса: реальный: 0, действующий: 0 Идентификаторы группы текущего процесса: реальный: 0, действующий: 0 Количество дополнительных групп текущего процесса: 7 Идентификаторы дополнительных групп текущего процесса: 0 1 2 3 4 6 10 Идентификаторы пользователя текущего процесса после первой смены: реальный: 1, действующий: 1 setuid (uid): Operation not permitted Идентификаторы пользователя текущего процесса после второй смены: реальный: 1, действующий: 1 Идентификаторы пользователя текущего процесса после третьей смены: реальный: 1, действующий: 1
Утерять статус суперпользователя легко, а вернуть трудно...
Наконец, сделаем владельцем выполнимого файла рассматриваемой
программы пользователя с идентификатором 1, то же проделаем с
владеющей группой, взведем в режиме этого файла chmod ug+s ) и вновь
запустим его от имени обычного пользователя (см. листинг 7.16).
Идентификаторы пользователя текущего процесса: реальный: 108, действующий: 1 Идентификаторы группы текущего процесса: реальный: 3, действующий: 1 Количество дополнительных групп текущего процесса: 1 Идентификаторы дополнительных групп текущего процесса: 3 Идентификаторы пользователя текущего процесса после первой смены: реальный: 108, действующий: 1 Идентификаторы пользователя текущего процесса после второй смены: реальный: 108, действующий: 108 Идентификаторы пользователя текущего процесса после третьей смены: реальный: 108, действующий: 1
Видно, что сохраненный
Отметим, что если функция getgroups() применяется в
программе несколько раз, то память под массив supgroupslist лучше отвести
по максимуму, воспользовавшись выражением вида nsupgroups =
sysconf (_SC_NGROUPS_MAX) + 1;.
Для опроса и/или изменения umask:
umask [-S] [маска]
и одноименная функция (см. листинг 7.17).
#include <sys/stat.h>> mode_t umask (mode_t cmask);
Согласно стандарту, в маске могут фигурировать только биты режима доступа к файлам; трактовка прочих бит зависит от реализации.
Для служебной программы umask маска может задаваться и
выводиться в символьной и восьмеричной формах. Маска в символьной форме
является "позитивной" в том смысле, что заданные в ней umask аналогичен
утилите chmod.
Опция -S предписывает выдавать маску в символьной форме.
Функция umask() устанавливает новую ("негативную") маску и
возвращает старую.
Пример. Последовательность команд, показанная в листинге 7.18, выдаст результат, воспроизведенный в листинге 7.19.
umask 0 umask -S umask -- -x umask
u=rwx,g=rwx,o=rwx 0111
Обратим внимание на употребление признака конца опций --. Он
позволяет задавать аргументы, начинающиеся с минуса. Отметим также,
что результаты, выдаваемые umask, могут в дальнейшем
использоваться как аргументы данной утилиты (и в символьной, и в восьмеричных
формах).
Новые процессы создаются при помощи функции fork() (см. листинг 7.20).
#include <unistd.h> pid_t fork (void);
Новый (порожденный) процесс является точной копией процесса,
вызвавшего fork() (родительского), за исключением следующих
моментов.
fork().В случае успешного завершения функция fork() возвращает
порожденному процессу 0, а родительскому процессу – идентификатор
порожденного процесса. После этого оба процесса начинают
независимо выполнять инструкции, расположенные за обращением к fork().
При неудаче родительскому процессу возвращается -1, новый процесс
не создается.
Поскольку возвращаемые функцией fork() значения различны
для обеих копий, родительский и порожденный процессы могут далее
выполняться по-разному. Например, процесс-предок переходит в
состояние ожидания завершения процесса-потомка либо, если
процесс-потомок запущен асинхронно, продолжает выполнение параллельно с ним.
Процесс-потомок при помощи функции семейства exec() подменяет
программу, которая определяет поведение процесса, и передает ей
управление и список аргументов.
Напомним, что заголовок функции main() C-программы выглядит
в общем случае так, как показано в листинге 7.21.
int main (int argc, char *argv []);
Значение argc равно количеству аргументов; argv – это массив
указателей собственно на аргументы, которые определяются исходя из
командной строки, запускающей C-программу. В соответствии с
принятым соглашением, значение argc не меньше единицы, а первый элемент
массива argv указывает на цепочку символов, содержащую имя
выполняемого файла.
Аналогичный смысл имеют аргументы функций семейства exec()
(см. листинг 7.22).
#include <unistd.h> extern char **environ; int execl (const char *path, const char *arg0, ... /*, (char *) 0 */); int execv (const char *path, char *const argv []); int execle (const char *path, const char *arg0, ... /*, (char *) 0, char *const envp [] */); int execve (const char *path, char *const argv [], char *const envp []); int execlp (const char *file, const char *arg0, ... /*, (char *) 0 */); int execvp (const char *file, char *const argv []);
Функции семейства exec() заменяют текущий образ процесса
новым (и, следовательно, в случае успешного завершения возврат в
вызывающий процесс невозможен). Новый образ создается на основе выполнимого
файла, называемого файлом образа процесса.
Переменная environ инициализируется как указатель на массив
указателей на составляющие окружение цепочки символов. Массивы argv и environ завершаются пустым указателем.
Аргумент path указывает на
Аргумент file имеет аналогичный смысл, однако, если он задан как
простое имя, то производится поиск в каталогах, заданных переменной
окружения PATH.
Аргументы arg0, ..., являются указателями на цепочки символов,
составляющие список аргументов нового образа процесса. Последним в
списке располагается пустой указатель, а аргумент arg0 должен
указывать на имя файла-образа.
Аргумент envp имеет тот же смысл и назначение, что и переменная environ.
Файловые дескрипторы остаются открытыми в новом образе, если
только они не были снабжены флагом FD_CLOEXEC.
Если у файла с новым образом процесса взведен
Следующие
Родительский процесс реализует ожидание завершения
процессов-потомков и получает информацию о его (завершения) статусе с
помощью функций семейства wait() (см. листинг 7.23). Если
информация о статусе завершения была доступна до вызова wait(),
родительский процесс не приостанавливается, возврат из wait() происходит
немедленно.
#include <sys/wait.h> pid_t wait (int *stat_loc); pid_t waitpid (pid_t pid, int *stat_loc, int options);
Функция waitpid() эквивалентна wait(), если аргумент pid
равен (pid_t) (-1), а аргумент options имеет нулевое значение.
Аргумент pid задает набор порожденных процессов, (pid_t) (-1) представляет
произвольный элемент множества порожденных процессов. Если pid > 0>,
имеется в виду один процесс с данным идентификатором. Нулевое значение
специфицирует любого потомка из той же группы процессов, что и
вызывающий. Наконец, при pid < (pid_t) (-1) запрашивается статус
завершения любого порожденного процесса из группы, идентификатор
которой равен абсолютной величине pid.
Аргумент options задается как побитное ИЛИ следующих флагов,
определенных в заголовочном файле <sys/wait.h>.
WNOHANG
Функция waitpid() не приостанавливает выполнение вызывающего
потока управления, если запрашиваемый статус не доступен немедленно.
WUNTRACED
Наряду со статусом завершения запрашивать статус остановленных, но еще не опрошенных процессов.
Если запрос статуса порожденного процесса завершился успешно,
функции wait() и waitpid() возвращают идентификатор этого
процесса и размещают по указателю stat_loc (если он отличен от NULL )
значение, которое будет нулевым тогда и только тогда, когда выдан статус
порожденного процесса, завершившегося по одной из трех причин:
main с нулевым результатом;_exit() или exit() с
нулевым аргументом;Для анализа целочисленного значения stat_val, на которое
указывает аргумент stat_loc, в файле <sys/wait.h> определен набор макросов.
Например, значение WIFEXITED (stat_val ) будет ненулевым в случае
нормального завершения порожденного процесса; при этом WEXITSTATUS
(stat_val) возвращает младший байт статуса. Макрос WIFSTOPPED
(stat_val) возвращает ненулевое значение, если получен статус остановленного процесса.
Процесс может вызвать собственное завершение, обратившись к
функциям семейства exit() (см. листинг 7.24).
#include <stdlib.h> void exit (int status); void _Exit (int status); #include <unistd.h> void _exit (int status);
Значением аргумента status могут служить константы 0, EXIT_SUCCESS, EXIT_FAILURE или любое другое значение, хотя ожидающему
родительскому процессу будет доступен только младший байт статуса ( status 0377 ).
Функция exit(), в отличие от _Exit() и _exit(), производит
аккуратное завершение: выполняет зарегистрированные функции (см. ниже
описание функции atexit() ), выталкивает буфера, закрывает потоки,
удаляет временные файлы и т.д. Функции _Exit() и _exit()
эквивалентны. Все функции семейства exit() закрывают файловые
дескрипторы, открытые вызывающим процессом.
Если родительский процесс выполняет функции wait() или waitpid(), ему передается младший байт значения status. Если
родительский процесс этого не делает, функции семейства exit() переводят
вызывающий процесс в состояние зомби.
Если у завершающегося процесса были потомки, родительским для них становится системный процесс, определяемый реализацией.
Функция atexit() (см. листинг 7.25) позволяет зарегистрировать
функции, которые будут вызываться, если процесс завершается,
обращаясь к exit() или возвращаясь из main().
#include <stdlib.h> int atexit (void (*func) (void));
Реализация должна поддерживать регистрацию по крайней мере тридцати двух функций и вызывать их в обратном порядке.
В листинге 7.26 приведен пример использования функций порождения и завершения процессов.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
static void atefunc (void) {
/* Перед завершением выдадим информацию о */
/* процессах */
printf ("Ситуация перед завершением
родительского процесса\n");
(void) system ("ps -o pid,ppid,vsz,args");
}
int main (void) {
int pid;
int stat;
/* Отменим буферизацию стандартного вывода */
setbuf (stdout, NULL);
/* Зарегистрируем обработчик завершения процесса */
if (atexit (atefunc) != 0) {
perror ("ATEXIT");
exit (1);
}
/* Создадим новый процесс */
if ((pid = fork ()) < 0) {
perror ("FORK");
exit (2);
} else if (pid == 0) {
/* Порожденный процесс */
/* Выполним служебную программу ps */
printf ("Ситуация с точки зрения порожденного
процесса\n");
(void) execl ("/bin/ps", "ps", "-o",
"pid,ppid,args", (char *) 0);
perror ("EXEC");
exit (3); /* execl() завершился неудачей */
} else {
/* Родительский процесс */
sleep (1);
/* Вероятно, порожденный процесс уже */
/* завершился */
/* Посмотрим на текущую ситуацию */
printf ("Ситуация перед вызовом waitpid() в
родительском процессе\n");
(void) system ("ps -o pid,ppid,vsz,args");
(void) waitpid (pid, stat, 0);
printf ("Статус завершения порожденного
процесса с идентификатором %d: %d\n", pid, stat);
}
return 0;
}
Результат работы этой программы может выглядеть так,
как показано в листинге 7.27 (выполнимый файл программы имеет имя prog30 ).
Ситуация с точки зрения порожденного процесса PID PPID COMMAND 6123 1072 -sh 29568 6123 prog30 29569 29568 ps -o pid,ppid,args Ситуация перед вызовом waitpid() в родительском процессе PID PPID VSZ COMMAND 6123 1072 2260 -sh 29568 6123 1340 prog30 29569 29568 0 [ps <defunct>] 29570 29568 2584 ps -o pid,ppid,vsz,args Статус завершения порожденного процесса с идентификатором 29569: 0 Ситуация перед завершением родительского процесса PID PPID VSZ COMMAND 6123 1072 2260 -sh 29568 6123 1340 prog30 29571 29568 2584 ps -o pid,ppid,vsz,args
Обратим внимание на информацию о зомби-процессе (с пометкой <defunct> ), а также на то, что функция, зарегистрированная с помощью atexit(), вызвана в результате возврата из main().
Для терминирования процессов извне предназначена служебная
программа kill, вызванная следующим образом:
kill -s TERM идентификатор_процесса ...
Если это не помогло, можно применить более сильный вариант:
kill -s KILL идентификатор_процесса ...
Разумеется, попытки терминирования процессов подвержены контролю прав доступа.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.