Программирование в стандарте POSIX

       

Создание и завершение процессов


Новые процессы создаются при помощи функции fork() (см. листинг 7.20).

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

Листинг 7.20. Описание функции fork(). (html, txt)

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

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

В случае успешного завершения функция fork() возвращает порожденному процессу 0, а родительскому процессу – идентификатор порожденного процесса. После этого оба процесса начинают независимо выполнять инструкции, расположенные за обращением к fork(). При неудаче родительскому процессу возвращается -1, новый процесс не создается.

Поскольку возвращаемые функцией fork() значения различны для обеих копий, родительский и порожденный процессы могут далее выполняться по-разному. Например, процесс-предок переходит в состояние ожидания завершения процесса-потомка либо, если процесс-потомок запущен асинхронно, продолжает выполнение параллельно с ним. Процесс-потомок при помощи функции семейства exec() подменяет программу, которая определяет поведение процесса, и передает ей управление и список аргументов.

Напомним, что заголовок функции main() C-программы выглядит в общем случае так, как показано в листинге 7.21.

int main (int argc, char *argv []);

Пример 7.21. Заголовок функции main() C-программы. (html, txt)

Значение argc равно количеству аргументов; argv – это массив указателей собственно на аргументы, которые определяются исходя из командной строки, запускающей C-программу.
В соответствии с принятым соглашением, значение argc не меньше единицы, а первый элемент массива argv указывает на цепочку символов, содержащую имя выполняемого файла.

Аналогичный смысл имеют аргументы функций семейства exec() (см. листинг 7.22).

Пример 7.22. Описание функций семейства exec(). (html, txt)

Функции семейства 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);

Пример 7.23. Описание функций семейства wait(). (html, txt)

Функция waitpid() эквивалентна wait(), если аргумент pid равен (pid_t) (-1), а аргумент options имеет нулевое значение. Аргумент pid задает набор порожденных процессов, статус завершения которых запрашивается. Значение (pid_t) (-1) представляет произвольный элемент множества порожденных процессов. Если pid > 0>, имеется в виду один процесс с данным идентификатором. Нулевое значение специфицирует любого потомка из той же группы процессов, что и вызывающий. Наконец, при pid < (pid_t) (-1) запрашивается статус завершения любого порожденного процесса из группы, идентификатор которой равен абсолютной величине pid.


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