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() приведен ниже (рис. 1):

#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");

  }

}

Рис. 1. Пример вызова fork().

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

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

Рассмотрим следующий пример (рис. 2).

#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);

}

Рис. 2. Порождение нескольких потомков.

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

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


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