next up previous contents
Next: Запуск процессов с помощью Up: Процессы Previous: Таблица процессов   Contents

Создание процессов с помощью вызова fork().

Для порождения процессов в ОС Linux существует два способа. Один из них позволяет полностью заменить другой процесс, без замены среды выполнения. Другим способом можно создать новый процесс с помощью системного вызова fork(). Синтаксис вызова следующий:

#include <sys/types> #include <unistd.h> pid_t fork(void); pid_t является примитивным типом данных, который определяет идентификатор процесса или группы процессов. При вызове fork() порождается новый процесс (процесс-потомок), который почти идентичен порождающему процессу-родителю. Процесс-потомок наследует следующие признаки родителя:

Потомок не наследует от родителя следующих признаков: При вызове fork() возникают два полностью идентичных процесса. Весь код после fork() выполняется дважды, как в процессе-потомке, так и в процессе-родителе.

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

Таким образом, можно проверить, был ли создан новый процесс:

switch(ret=fork()) { case -1: /{*}при вызове fork() возникла ошибка{*}/ case 0 : /{*}это код потомка{*}/ default : /{*}это код родительского процесса{*}/ } Пример порождения процесса через fork() приведен ниже: #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> main() { pid_t pid; int rv; switch(pid=fork()) { case -1: perror("fork"); /* произошла ошибка */ exit(1); /*выход из родительского процесса*/ case 0: printf(" CHILD: Это процесс-потомок!\n"); printf(" CHILD: Мой PID -- %d\n", getpid()); printf(" CHILD: PID моего родителя -- %d\n", getppid()); printf(" CHILD: Введите мой код возврата (как можно меньше):"); scanf(" %d"); printf(" CHILD: Выход!\n"); exit(rv); default: printf("PARENT: Это процесс-родитель!\n"); printf("PARENT: Мой PID -- %d\n", getpid()); printf("PARENT: PID моего потомка %d\n",pid); printf("PARENT: Я жду, пока потомок не вызовет exit()...\n"); wait(); printf("PARENT: Код возврата потомка:%d\n", WEXITSTATUS(rv)); printf("PARENT: Выход!\n"); } }

Когда потомок вызывает exit(), код возврата передается родителю, который ожидает его, вызывая wait(). WEXITSTATUS() представляет собой макрос, который получает фактический код возврата потомка из вызова wait().

Функция wait() ждет завершения первого из всех возможных потомков родительского процесса. Иногда необходимо точно определить, какой из потомков должен завершиться. Для этого используется вызов waitpid() с соответствующим PID потомка в качестве аргумента. Еще один момент, на который следует обратить внимание при анализе примера, это то, что и родитель, и потомок используют переменную rv. Это не означает, что переменная разделена между процессами. Каждый процесс содержит собственные копии всех переменных.

Рассмотрим следующий пример: #include <sys/types.h> #include <stdio.h> #include <unistd.h> int main() { char pid{[}255{]}; fork(); fork(); fork(); sprintf(pid, "PID : %d\n",getpid()); write(STDOUT_FILENO, pid, strlen(pid)); exit(0); }

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

Так называемые процессы-зомби возникают, если потомок завершился, а родительский процесс не вызвал wait(). Для завершения процессов используют либо оператор возврата, либо вызов функции exit() со значением, которое нужно возвратить операционной системе. Операционная система оставляет процесс зарегистрированным в своей внутренней таблице данных, пока родительский процесс не получит кода возврата потомка, либо не закончится сам. В случае процесса-зомби его код возврата не передается родителю, и запись об этом процессе не удаляется из таблицы процессов операционной системы. При дальнейшей работе и появлении новых зомби таблица процессов может быть заполнена, что приведет к невозможности создания новых процессов.



Subsections

2004-06-22