Создание и прекращение процесса

До сих пор мы знаем, что всякий раз, когда мы выполняем программу, процесс создается и будет завершен после завершения выполнения. Что делать, если нам нужно создать процесс в программе, и может потребоваться запланировать для него другую задачу. Можно ли этого достичь? Да, очевидно, через создание процесса. Конечно, после того, как задание выполнено, оно будет прервано автоматически, или вы можете прекратить его при необходимости.

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

Системный вызов

После создания дочернего процесса, давайте посмотрим детали системного вызова fork ().

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

Создает дочерний процесс. После этого вызова есть два процесса: существующий называется родительским, а вновь созданный - дочерним.

Системный вызов fork () возвращает одно из трех значений:

  • Отрицательное значение, указывающее на ошибку, т. Е. Не удалось создать дочерний процесс.

  • Возвращает ноль для дочернего процесса.

  • Возвращает положительное значение для родительского процесса. Это значение является идентификатором недавно созданного дочернего процесса.

Давайте рассмотрим простую программу.

File name: basicfork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
   fork();
   printf("Called fork() system call\n");
   return 0;
}

Шаги выполнения

компиляция

gcc basicfork.c -o basicfork

Исполнение / выход

Called fork() system call
Called fork() system call

Примечание. Обычно после вызова fork () дочерний процесс и родительский процесс выполняют разные задачи. Если нужно выполнить одну и ту же задачу, то для каждого вызова fork () она будет выполняться 2 раза по n раз, где n - количество вызовов fork ().

В приведенном выше случае fork () вызывается один раз, поэтому вывод выводится дважды (2 степени 1). Если fork () вызывается, скажем, 3 раза, то вывод будет напечатан 8 раз (2 степени 3). Если он вызывается 5 раз, то печатается 32 раза и так далее, и так далее.

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

Имя файла: pids_after_fork.c

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
   pid_t pid, mypid, myppid;
   pid = getpid();
   printf("Before fork: Process id is %d\n", pid);
   pid = fork();

   if (pid < 0) {
      perror("fork() failure\n");
      return 1;
   }

   // Child process
   if (pid == 0) {
      printf("This is child process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
   } else { // Parent process 
      sleep(2);
      printf("This is parent process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
      printf("Newly created process id or child pid is %d\n", pid);
   }
   return 0;
}

Шаги компиляции и выполнения

Before fork: Process id is 166629
This is child process
Process id is 166630 and PPID is 166629
Before fork: Process id is 166629
This is parent process
Process id is 166629 and PPID is 166628
Newly created process id or child pid is 166630

Процесс может завершиться любым из двух способов -

  • Аномально, происходит при доставке определенных сигналов, скажем, сигнала завершения.

  • Обычно используется системный вызов _exit () (или системный вызов _Exit ()) или библиотечная функция exit ().

Разница между _exit () и exit () заключается в основном в очистке. Функция exit () выполняет некоторую очистку перед возвратом элемента управления ядру, тогда как _exit () (или _Exit ()) немедленно возвращает элемент обратно ядру.

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

Имя файла: atexit_sample.c

#include <stdio.h>
#include <stdlib.h>

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   exit (0);
}

Шаги компиляции и выполнения

Hello, World!
Called cleanup function - exitfunc()

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

Имя файла: at_exit_sample.c

#include <stdio.h>
#include <unistd.h>

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   _exit (0);
}

Шаги компиляции и выполнения

Hello, World!