Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2557 / 712 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 7:

Процессы

< Лекция 6 || Лекция 7: 123456 || Лекция 8 >

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

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

#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-программы выглядит в общем случае так, как показано в листинге 7.21.

int main (int argc, char *argv []);
7.21. Заголовок функции main() C-программы.

Значение 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 []);
7.22. Описание функций семейства exec().

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

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

< Лекция 6 || Лекция 7: 123456 || Лекция 8 >
Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?