Creación y terminación de procesos

Hasta ahora sabemos que cada vez que ejecutamos un programa, se crea un proceso y se terminará una vez completada la ejecución. ¿Qué sucede si necesitamos crear un proceso dentro del programa y es posible que deseemos programar una tarea diferente para él? ¿Se puede lograr esto? Sí, obviamente a través de la creación de procesos. Por supuesto, una vez finalizado el trabajo, se cancelará automáticamente o puede finalizarlo según sea necesario.

La creación de procesos se logra a través del fork() system call. El proceso recién creado se llama proceso hijo y el proceso que lo inició (o el proceso cuando se inicia la ejecución) se llama proceso padre. Después de la llamada al sistema fork (), ahora tenemos dos procesos: procesos padre e hijo. ¿Cómo diferenciarlos? Muy simple, es a través de sus valores de retorno.

Después de la creación del proceso hijo, veamos los detalles de la llamada al sistema fork ().

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

pid_t fork(void);

Crea el proceso hijo. Después de esta llamada, hay dos procesos, el existente se llama proceso padre y el recién creado se llama proceso hijo.

La llamada al sistema fork () devuelve cualquiera de los tres valores:

  • Valor negativo para indicar un error, es decir, no se pudo crear el proceso hijo.

  • Devuelve un cero para el proceso hijo.

  • Devuelve un valor positivo para el proceso padre. Este valor es el ID de proceso del proceso hijo recién creado.

Consideremos un programa simple.

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;
}

Pasos de ejecución

Compilacion

gcc basicfork.c -o basicfork

Ejecución / Salida

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

Note- Por lo general, después de la llamada a fork (), el proceso hijo y el proceso padre realizarían tareas diferentes. Si es necesario ejecutar la misma tarea, entonces para cada llamada a fork () se ejecutará 2 power n veces, donden es el número de veces que se invoca a fork ().

En el caso anterior, fork () se llama una vez, por lo tanto, la salida se imprime dos veces (2 potencia 1). Si se llama a fork (), digamos 3 veces, la salida se imprimirá 8 veces (2 potencia 3). Si se llama 5 veces, se imprime 32 veces y así sucesivamente.

Habiendo visto a fork () crear el proceso hijo, es hora de ver los detalles de los procesos padre e hijo.

Nombre de archivo: 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;
}

Pasos de compilación y ejecución

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

Un proceso puede terminar de dos formas:

  • De forma anormal, se produce al recibir ciertas señales, por ejemplo, señal de terminación.

  • Normalmente, se usa la llamada al sistema _exit () (o la llamada al sistema _Salir ()) o la función de biblioteca exit ().

La diferencia entre _exit () y exit () es principalmente la actividad de limpieza. losexit() hace una limpieza antes de devolver el control al kernel, mientras que el _exit() (o _Salir ()) devolvería el control al núcleo inmediatamente.

Considere el siguiente programa de ejemplo con exit ().

Nombre de archivo: 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);
}

Pasos de compilación y ejecución

Hello, World!
Called cleanup function - exitfunc()

Considere el siguiente programa de ejemplo con _exit ().

Nombre de archivo: 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);
}

Pasos de compilación y ejecución

Hello, World!