учебники, программирование, основы, введение в,

 

Процессы

Основные понятия
Напомним данное в стандарте POSIX-2001 определение процесса. Процесс – это адресное пространство вместе с выполняемыми в нем потоками управления, а также системными ресурсами, которые этим потокам требуются.
Каждый процесс обладает целым рядом атрибутов. Важнейшим среди них является идентификатор процесса – положительное целое число, однозначно идентифицирующее процесс в течение времени его жизни.
Процессы могут создаваться и завершаться. Время жизни процесса – это период от его создания до возврата идентификатора операционной системе.
После того как процесс создан с помощью функции fork(), он считается активным. До завершения процесса в его рамках существуют по крайней мере один поток управления и адресное пространство.
Процесс может перейти в неактивное состояние, и тогда некоторые из его ресурсов (но не идентификатор) могут быть возвращены системе. Когда по отношению к неактивному процессу выполняют функцию семейства wait(), системе возвращаются остальные ресурсы. Последний из них – идентификатор процесса, и на этом время жизни процесса заканчивается.
Завершение процесса может быть нормальным или аварийным. Нормальное завершение происходит, в частности, при возврате из функции main().
Зомби-процесс – завершившийся процесс, подлежащий ликвидации после того, как код его завершения будет передан ожидающему этого другому процессу.
Процесс, создавший данный, называется родительским, в течение времени жизни которого существует идентификатор родительского процесса. По завершении времени жизни указанного процесса родительским становится определяемый реализацией системный процесс.
Группа – совокупность процессов, допускающая согласованную доставку сигналов. У каждой группы имеется уникальный положительный целочисленный идентификатор, представляющий ее в течение времени ее жизни. В такой роли выступает идентификатор процесса, именуемого лидером группы.
Временем жизни группы процессов называют период от создания группы до момента, когда ее покидает последний процесс (по причине завершения или смены группы).
Задание – это набор процессов, составляющих конвейер, а также порожденных ими процессов, входящих в одну группу.
Под управлением заданиями подразумеваются предоставленные пользователям средства выборочно (при)останавливать и затем продолжать (возобновлять) выполнение процессов. На отдельные задания ссылаются с помощью идентификаторов.
Сеансом называется множество групп процессов, сформированное для целей управления заданиями. Каждая группа принадлежит некоторому сеансу; считается, что все процессы группы принадлежат тому же сеансу. Вновь созданный процесс присоединяется к сеансу своего создателя; в дальнейшем принадлежность сеансу может быть изменена.
Время жизни сеанса представляет собой период от создания сеанса до истечения времени жизни всех групп процессов, принадлежавших сеансу.
Лидер сеанса – процесс, создавший данный сеанс.
Управляющим терминалом называется терминал, ассоциированный с сеансом. У сеанса может быть не более одного управляющего терминала, а тот, в свою очередь, ассоциируется ровно с одним сеансом. Некоторые последовательности символов, вводимые с управляющего терминала, вызывают посылку сигналов всем процессам группы, ассоциированной с данным управляющим терминалом.
Управляющий процесс – это лидер сеанса, установивший соединение с управляющим терминалом. Если в дальнейшем терминал перестанет быть управляющим для сеанса, лидер сеанса утратит статус управляющего процесса.
Задания, группы процессов и процессы подразделяются на приоритетные (переднего плана) и фоновые. Процессы переднего плана, в отличие от фоновых, имеют некоторые привилегии при доступе к управляющему терминалу. В сеансе, установившем соединение с управляющим терминалом, может быть не более одной группы процессов, приоритетной по отношению к данному управляющему терминалу.
С каждым процессом ассоциируется идентификатор создавшего его пользователя. Этот атрибут называется реальным идентификатором пользователя процесса.
В момент создания процесса пользователь входил в некоторую группу, идентификатор которой называется реальным идентификатором группы процесса.
Для определения прав процесса (в том числе прав доступа к файлам) применяются действующие идентификаторы пользователя и группы , которые в общем случае могут отличаться от реальных.
Поведение процесса определяется исполняемой в его рамках программой – последовательностью инструкций для решения определенной задачи. Программы хранятся в файлах. В режим файлов входят биты переустановки действующих идентификаторов пользователя (ПДИП) и группы (ПДИГ). Если эти биты взведены, то при запуске программы на выполнение действующие идентификаторы пользователя и группы процесса могут наследоваться у файла.
Стандартом POSIX-2001 предусмотрены также сохраненные переустановленные действующие идентификаторы пользователя (сохраненные ПДП-идентификаторы) и группы (сохраненные ПДГ-идентификаторы) процесса. Эти атрибуты расширяют возможности, связанные со сменой действующих идентификаторов пользователя и группы процесса.
При определении прав доступа к файлам наравне с действующим идентификатором группы процесса используются идентификаторы дополнительных групп.
С процессом ассоциируются маска режима создания файлов, влияющая на устанавливаемый режим доступа к новым файлам, и ограничение на размер записываемых файлов.
Текущий и корневой каталоги и набор дескрипторов открытых файлов также относятся к числу атрибутов процессов.
Все перечисленные выше атрибуты разделяются существующими в рамках процесса потоками управления. К числу индивидуальных атрибутов относятся идентификатор, приоритет и политика планирования, значение переменной errno, ассоциированные с потоком управления пары ключ/значение, а также системные ресурсы, требующиеся для поддержки потока управления.
Всем потокам управления одного процесса доступны все объекты, адреса которых могут быть определены потоком. В число таких объектов входят статические переменные, полученные от функции malloc() области динамической памяти, унаследованная от системно-зависимых функций прямоадресуемая память, автоматические переменные и т.д.
По отношению к потокам управления вводится понятие безопасных функций, которые можно вызывать параллельно в нескольких потоках без нарушения корректности их функционирования. К числу безопасных принадлежат «чистые» функции, а также функции, обеспечивающие взаимное исключение перед доступом к разделяемым объектам. Если в стандарте явно не оговорено противное, функция считается потоковобезопасной.
Выполняющимся (активным) называется поток управления, обрабатываемый в данный момент процессором. В многопроцессорных конфигурациях может одновременно выполняться несколько потоков.
Поток управления считается готовым к выполнению, если он способен стать активным, но не может этого сделать из-за отсутствия свободного процессора.
Поток управления называют вытесненным, когда его выполнение приостановлено из-за того, что другой поток с более высоким приоритетом уже готов к выполнению.
Процесс (поток управления) считается блокированным, если для продолжения его выполнения должно стать истинным некоторое условие, отличное от доступности процессора.
Список потоков управления – это упорядоченный набор готовых к выполнению равноприоритетных потоков, очередность которых определяется политикой планирования. Множество наборов включает все потоки в системе, готовые к выполнению.
Планированием, согласно стандарту POSIX-2001, называется применение политики изменения списков потоков управления, а также политики выбора процесса или готового к выполнению потока управления для перевода как того, так и другого в число активных.
Под политикой планирования понимается набор правил, используемых для определения порядка выполнения процессов или потоков управления при достижении некоторой цели.
Политика планирования воздействует на порядок процессов (потоков управления) по крайней мере в следующих ситуациях:

  • когда активный процесс (поток управления) блокируется или вытесняется;
  • когда блокированный процесс (поток управления) становится готовым к выполнению.

Область планирования размещения – это набор процессоров, по отношению к которым в какой-то момент времени планируется поток управления.
Областью планирования конкуренции называется свойство потока управления, определяющее набор потоков, с которыми он конкурирует за ресурсы (например, за процессор). В стандарте POSIX-2001 предусмотрены две подобные области – PTHREAD_SCOPE_SYSTEM и PTHREAD_SCOPE_PROCESS.
Данные выше определения будут поясняться и раскрываться по мере описания служебных программ и функций, обслуживающих понятие процесса.

http://localhost:3232/img/empty.gifОпрос и изменение атрибутов процессов

Для выдачи информации о процессах служит утилита ps:
ps [-aA] [-defl] [-G список_групп]
[-o формат] ... [-p список_процессов]
[-t список_терминалов]
[-U список_пользователей]
-g список_групп]
[-n список_имен]
[-u список_пользователей]
По умолчанию информация выдается обо всех процессах, имеющих тот же действующий идентификатор и тот же управляющий терминал, что и у текущего пользователя. При этом выводятся идентификатор процесса, имя управляющего терминала, истраченное к данному моменту процессорное время и имя программы (команды), выполняемой в рамках процесса. Например, выдача команды ps может выглядеть так, как показано в.
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
Листинг 7.1. Возможный результат использования утилиты ps.
Если нужна более подробная информация о более широком наборе процессов, следует пользоваться опциями. Перечислим наиболее употребительные из них.
-a
Выдать информацию обо всех процессах, ассоциированных с терминалами. Заметим, однако, что, во-первых, при получении информации о процессах контролируются права доступа (например, пользователю будут видны только порожденные им процессы), а во-вторых, по стандарту реализация может не включать в выдаваемый список лидеров сеансов.
-A
Выдать информацию обо всех процессах.
-G список_групп
Выдать информацию о процессах с заданными реальными идентификаторами групп.
-o формат
Выдать информацию о процессах в заданном формате.
-p список_процессов
Выдать информацию о процессах с заданными идентификаторами.
-t список_терминалов
Выдать информацию о процессах, ассоциированных с заданными терминалами. Способ задания терминалов зависит от реализации. Обычно указывается имя специального файла, например, ttyS4, или, если имя начинается с tty, просто S4.
-U список_пользователей
Выдать информацию о процессах с заданными реальными идентификаторами пользователей (они могут указываться и в виде входных имен).
Все перечисленные опции, кроме -o, ведают отбором процессов. Если задан целый ряд подобных опций, выводится информация обо всех специфицированных ими процессах.
Опции -o (их в командной строке может быть несколько) позволяют задать выходной формат информации о процессах. Указываются выводимые поля и, если нужно, отличные от подразумеваемых тексты соответствующих им заголовков, отделяющиеся от имени поля знаком равенства и продолжающиеся до конца аргумента опции -o.
Перечислим имена полей, которые могут указываться в выходном формате, и соответствующие им подразумеваемые заголовки.
ruser (RUSER)
Выдавать реальный идентификатор пользователя процесса (в символьной или числовой форме).
user (USER)
Действующий идентификатор пользователя процесса.
rgroup (RGROUP)
Реальный идентификатор группы процесса.
group (GROUP)
Действующий идентификатор группы процесса.
pid (PID)
Идентификатор процесса.
ppid (PPID)
Идентификатор родительского процесса.
pgid (PGID)
Идентификатор группы процессов.
pcpu (%CPU)
Процент процессорного времени, потребляемый процессом.
vsz (VSZ)
Размер процесса в (виртуальной) памяти (в килобайтных блоках).
nice (NI)
Число, используемое как рекомендация системе при планировании процессов. Меньшие значения соответствуют более приоритетным процессам.
etime (ELAPSED)
Астрономическое время, прошедшее с момента запуска процесса.
time (TIME)
Процессорное время, потребленное процессом.
tty (TT)
Имя управляющего терминала.
comm (COMMAND)
Имя выполняемой команды (argv [0]).
args (COMMAND)
Выполняемая командная строка.
http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gifНапример, командная строка, показанная в, породит выдачу, фрагмент которой приведен в
ps -A -o ruser,user,pid,ppid,tty=TTY -o
nice,vsz,args
Листинг 7.2. Пример использования утилиты ps.
Листинг 7.3. Фрагмент возможного результата использования утилиты ps.
Для опроса идентификаторов процесса, родительского процесса и группы процессов предусмотрены функции getpid() и getppid()   getpgrp().
#include <unistd.h>
pid_t getpid (void);

#include <unistd.h>
pid_t getppid (void);

#include <unistd.h>
pid_t getpgrp (void);
Листинг 7.4. getpid(), getppid() и getpgrp().
По стандарту эти функции всегда завершаются успешно, поэтому ошибочных кодов возврата не предусмотрено.
Для установки идентификатора группы процессов в целях управления заданиями предназначена функция setpgid().
#include <unistd.h>
int setpgid (pid_t pid, pid_t pgid);
Листинг 7.5. Описание функции setpgid().
Выполнение функции setpgid() влечет либо присоединение к существующей группе процессов, либо создание новой группы в рамках сеанса, в который входит вызывающий процесс. Процесс может установить идентификатор группы для себя или для порожденного процесса. Нельзя изменить идентификатор группы процессов лидера сеанса.
В случае успешного завершения функции setpgid() (результат при этом равен нулю) идентификатор группы процессов устанавливается равным pgid для заданного аргументом pid процесса. Если значение pid равно нулю, установка производится для вызывающего процесса. А если значение pgid равно нулю, то в качестве идентификатора группы процессов используется идентификатор процесса, заданного аргументом pid.
Для создания сеанса и установки идентификатора группы процессов служит функция setsid().
#include <unistd.h>
pid_t setsid (void);
Листинг 7.6. Описание функции setsid().
Если вызывающий процесс не является лидером группы, в результате выполнения функции setsid() будет создан новый сеанс, причем вызывающий процесс станет лидером этого сеанса, равно как и лидером новой группы процессов (без управляющего терминала и без других процессов в группе и сеансе).
Программа, показанная в, служит примером использования (в том числе некорректного) описанных функций. Возможный результат работы этой программы приведен в.
Листинг 7.7. Пример программы, использующей функции getpid(), getppid(), getpgrp(), setpgid(), setsid().
Листинг 7.8. Возможный результат работы программы, показанной в листинге 7.7.
Опрос реальных и действующих идентификаторов пользователя и группы вызывающего процесса осуществляется с помощью функций getuid(), geteuid(), getgid(), getegid() . Как и getpid(), они всегда завершаются успешно.
#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);
Листинг 7.9. Описание функций getuid(), geteuid(), getgid(), getegid().
Более сложный интерфейс имеет функция getgroups(), предназначенная для получения идентификаторов дополнительных групп вызывающего процесса . Эти идентификаторы (в их число может входить и действующий идентификатор группы процесса) помещаются в массив grouplist.
#include <unistd.h>
int getgroups (int gidsetsize,
gid_t grouplist []);
Листинг 7.10. Описание функции getgroups().
Аргумент gidsetsize задает число элементов в массиве grouplist, а реальное количество записанных идентификаторов групп возвращается в виде результата функции. Если в качестве значения gidsetsize задать нуль, getgroups() выдаст количество дополнительных групп, не модифицируя массив grouplist.
http://localhost:3232/img/empty.gifПереустановить действующий идентификатор пользователя вызывающего процесса позволяют функции setuid() и seteuid() (см.). Операция разрешена, если реальный или сохраненный ПДП-идентификатор пользователя совпадает со значением аргумента uid. Помимо этого, обладающие соответствующими привилегиями процессы с помощью функции setuid() могут установить по значению uid все три идентификатора пользователя процесса – реальный, действующий и сохраненный.
#include <unistd.h>
int setuid (uid_t uid);

#include <unistd.h>
int seteuid (uid_t uid);
Листинг 7.11. Описание функций setuid() и seteuid().
Для непривилегированных процессов по соображениям мобильности рекомендуется использование функции seteuid().
Аналогичные функции для переустановки идентификаторов группы процесса показаны в.
#include <unistd.h>
int setgid (gid_t gid);

#include <unistd.h>
int setegid (gid_t gid);
Листинг 7.12. Описание функций setgid() и setegid().
Отметим, что функция для изменения списка дополнительных групп setgroups() относится к числу привилегированных и, следовательно, остается за рамками стандарта POSIX.
Проиллюстрируем использование описанных функций с помощью программы, показанной в.
Листинг 7.13. Пример использования функций опроса и изменения идентификаторов пользователя процесса.
Если эту программу запустить от имени обычного пользователя, результат может выглядеть так, как показано в.
Листинг 7.14. Возможный результат работы программы, показанной в листинге 7.13 и запущенной от имени обычного пользователя.
После запуска той же программы от имени суперпользователя может получиться результат, показанный в.
Листинг 7.15. Возможный результат работы программы, показанной в листинге 7.13 и запущенной от имени суперпользователя.
Утерять статус суперпользователя легко, а вернуть трудно...
Наконец, сделаем владельцем выполнимого файла рассматриваемой программы пользователя с идентификатором 1, то же проделаем с владеющей группой, взведем в режиме этого файла биты ПДИП и ПДИГ(на ОС Linux можно воспользоваться командой chmod ug+s) и вновь запустим его от имени обычного пользователя .
Листинг 7.16. Возможный результат работы программы, показанной в листинге 7.13 и запущенной от имени обычного пользователя после взведения в режиме выполнимого файла бита ПДИП.
Видно, что сохраненный ПДП-идентификатор помогает непривилегированному процессу переустанавливать действующий идентификатор пользователя.
Отметим, что если функция getgroups() применяется в программе несколько раз, то память под массив supgroupslist лучше отвести по максимуму, воспользовавшись выражением вида nsupgroups = sysconf (_SC_NGROUPS_MAX) + 1;.
Для опроса и/или изменения маски режима создания файлов вызывающего процесса предназначены служебная программа umask:
umask [-S] [маска]
и одноименная функция .
#include <sys/stat.h>>
mode_t umask (mode_t cmask);
Листинг 7.17. Описание функции umask().
Согласно стандарту, в маске могут фигурировать только биты режима доступа к файлам; трактовка прочих бит зависит от реализации.
Для служебной программы umask маска может задаваться и выводиться в символьной и восьмеричной формах. Маска в символьной форме является «позитивной» в том смысле, что заданные в ней биты режима доступа будут сохранены при создании файла. Восьмеричная форма – «негативная», заданные в ней биты будут «вычитаться» (очищаться) из режима доступа, заданного при создании. Синтаксис маски в umask аналогичен утилите chmod.
Опция -S предписывает выдавать маску в символьной форме.
Функция umask() устанавливает новую («негативную») маску и возвращает старую.
Пример. Последовательность команд, показанная в, выдаст результат, воспроизведенный в.
umask 0
umask -S
umask -- -x
umask
Листинг 7.18. Пример использования служебной программы umask.
u=rwx,g=rwx,o=rwx
0111
Пример 7.19. Листинг 7.19. Возможный результат использования служебной программы umask.
Обратим внимание на употребление признака конца опций --. Он позволяет задавать аргументы, начинающиеся с минуса. Отметим также, что результаты, выдаваемые umask, могут в дальнейшем использоваться как аргументы данной утилиты (и в символьной, и в восьмеричных формах).
Создание и завершение процессов
Новые процессы создаются при помощи функции fork().
#include <unistd.h>
pid_t fork (void);
Листинг 7.20. Описание функции fork().
Новый (порожденный) процесс является точной копией процесса, вызвавшего fork() (родительского), за исключением следующих моментов.

  1. У порожденного процесса свой идентификатор, равно как и идентификатор родительского процесса.
  2. У порожденного процесса собственная копия файловых дескрипторов, ссылающихся на те же описания открытых файлов, что и соответствующие дескрипторы родительского процесса.
  3. Порожденный процесс не наследует блокировки файлов, установленные родительским процессом.
  4. Порожденный процесс создается с одним потоком управления – копией того, что вызвал fork().
  5. Имеются также некоторые тонкости, связанные с обработкой сигналов, на которых мы, однако, останавливаться не будем.

В случае успешного завершения функция fork() возвращает порожденному процессу 0, а родительскому процессу – идентификатор порожденного процесса. После этого оба процесса начинают независимо выполнять инструкции, расположенные за обращением к fork(). При неудаче родительскому процессу возвращается -1, новый процесс не создается.
Поскольку возвращаемые функцией fork() значения различны для обеих копий, родительский и порожденный процессы могут далее выполняться по-разному. Например, процесс-предок переходит в состояние ожидания завершения процесса-потомка либо, если процесс-потомок запущен асинхронно, продолжает выполнение параллельно с ним. Процесс-потомок при помощи функции семейства exec() подменяет программу, которая определяет поведение процесса, и передает ей управление и список аргументов.
Напомним, что заголовок функции main() C-программы выглядит в общем случае так, как показано в.
int main (int argc, char *argv []);
Пример 7.21. Заголовок функции main() C-программы.
Значение argc равно количеству аргументов; argv – это массив указателей собственно на аргументы, которые определяются исходя из командной строки, запускающей C-программу. В соответствии с принятым соглашением, значение argc не меньше единицы, а первый элемент массива argv указывает на цепочку символов, содержащую имя выполняемого файла.
Аналогичный смысл имеют аргументы функций семейства exec().
Пример 7.22. Описание функций семейства exec().
Функции семейства exec() заменяют текущий образ процесса новым (и, следовательно, в случае успешного завершения возврат в вызывающий процесс невозможен). Новый образ создается на основе выполнимого файла, называемого файлом образа процесса.
Переменная environ инициализируется как указатель на массив указателей на составляющие окружение цепочки символов. Массивы argv и environ завершаются пустым указателем.
Аргумент path указывает на маршрутное имя файла с новым образом процесса.
Аргумент file имеет аналогичный смысл, однако, если он задан как простое имя, то производится поиск в каталогах, заданных переменной окружения PATH.
Аргументы arg0, ..., являются указателями на цепочки символов, составляющие список аргументов нового образа процесса. Последним в списке располагается пустой указатель, а аргумент arg0 должен указывать на имя файла-образа.
Аргумент envp имеет тот же смысл и назначение, что и переменная environ.
Файловые дескрипторы остаются открытыми в новом образе, если только они не были снабжены флагом FD_CLOEXEC.
Если у файла с новым образом процесса взведен бит ПДИП, действующий идентификатор пользователя процесса переустанавливается равным идентификатору владельца файла (аналогично для группы).
Следующие атрибуты процесса остаются неизменными:

  • идентификатор процесса;
  • идентификатор родительского процесса;
  • идентификатор группы процессов;
  • членство в сеансе;
  • реальные идентификаторы пользователя и группы процесса;
  • идентификаторы дополнительных групп;
  • текущий и корневой каталоги;
  • маска режима создания файлов;
  • атрибуты, связанные с обработкой сигналов.

Родительский процесс реализует ожидание завершения процессов-потомков и получает информацию о его (завершения) статусе с помощью функций семейства wait(). Если информация о статусе завершения была доступна до вызова wait(), родительский процесс не приостанавливается, возврат из wait() происходит немедленно.
#include <sys/wait.h>
pid_t wait (int *stat_loc);
pid_t waitpid (pid_t pid, int *stat_loc,
int  options);
Пример 7.23. Описание функций семейства wait().
Функция waitpid() эквивалентна wait(), если аргумент pid равен (pid_t) (-1), а аргумент options имеет нулевое значение. Аргумент pid задает набор порожденных процессов, статус завершения которых запрашивается. Значение (pid_t) (-1) представляет произвольный элемент множества порожденных процессов. Если pid > 0>, имеется в виду один процесс с данным идентификатором. Нулевое значение специфицирует любого потомка из той же группы процессов, что и вызывающий. Наконец, при pid < (pid_t) (-1) запрашивается статус завершения любого порожденного процесса из группы, идентификатор которой равен абсолютной величине pid.
http://localhost:3232/img/empty.gifhttp://localhost:3232/img/empty.gifАргумент 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().
#include <stdlib.h>
void exit (int status);
void _Exit (int status);
#include <unistd.h>
void _exit (int status);
Пример 7.24. Описание функций семейства exit().
Значением аргумента status могут служить константы 0, EXIT_SUCCESS, EXIT_FAILURE или любое другое значение, хотя ожидающему родительскому процессу будет доступен только младший байт статуса (status & 0377).
Функция exit(), в отличие от _Exit() и _exit(), производит аккуратное завершение: выполняет зарегистрированные функции (см. ниже описание функции atexit()), выталкивает буфера, закрывает потоки, удаляет временные файлы и т.д. Функции _Exit() и _exit() эквивалентны. Все функции семейства exit() закрывают файловые дескрипторы, открытые вызывающим процессом.
Если родительский процесс выполняет функции wait() или waitpid(), ему передается младший байт значения status. Если родительский процесс этого не делает, функции семейства exit() переводят вызывающий процесс в состояние зомби.
Если у завершающегося процесса были потомки, родительским для них становится системный процесс, определяемый реализацией.
Функция atexit() позволяет зарегистрировать функции, которые будут вызываться, если процесс завершается, обращаясь к exit() или возвращаясь из main().
#include <stdlib.h>
int atexit (void (*func) (void));
Пример 7.25. Описание функции atexit().
Реализация должна поддерживать регистрацию по крайней мере тридцати двух функций и вызывать их в обратном порядке.
В приведен пример использования функций порождения и завершения процессов.
Пример 7.26. Пример использования функций порождения и за- вершения процессов.
Результат работы этой программы может выглядеть так, как показано в (выполнимый файл программы имеет имя prog30).
Пример 7.27. Возможный результат работы программы, использующей функции порождения и завершения процессов.
Обратим внимание на информацию о зомби-процессе (с пометкой <defunct>), а также на то, что функция, зарегистрированная с помощью atexit(), вызвана в результате возврата из main().
Для терминирования процессов извне предназначена служебная программа kill, вызванная следующим образом:
kill -s TERM идентификатор_процесса ...
Если это не помогло, можно применить более сильный вариант:
kill -s KILL идентификатор_процесса ...
Разумеется, попытки терминирования процессов подвержены контролю прав доступа.

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика