Fork, exec, wait y exit system call explicados en Linux
La secuencia de instrucciones y datos que se pueden ejecutar una sola vez, varias veces o simultáneamente se denominan programas. Y el proceso es la ejecución de dichos programas. Entonces esos procesos pueden ejecutar muchos programas. En el mismo proceso, el sistema operativo puede cargar diferentes programas. Los nuevos programas heredan los estados de proceso reutilizados como directorios actuales, privilegios, identificadores de archivos, etc. Estas cosas se hacen al mismo nivel que las llamadas al sistema como fork(), exec(), wait() y exit().
En este artículo, vamos a discutir las llamadas al sistema de Linux fork(), exec(), wait() y exit() en detalle con ejemplos y casos de uso.
tenedor()
El fork() es una de las llamadas al sistema que es muy especial y útil en los sistemas Linux/Unix. Los procesos lo utilizan para crear los procesos que son copias de sí mismos. Con la ayuda de dichas llamadas al sistema, el proceso principal puede crear el proceso secundario. Hasta que el proceso secundario se ejecute por completo, el proceso principal se suspende.
Algunos de los puntos importantes en fork() son los siguientes.
- El padre obtendrá el ID del proceso hijo con un valor distinto de cero.
- Valor cero se devuelve al niño.
- Si hay algún error de sistema o de hardware mientras se crea el hijo, se devuelve -1 a la bifurcación().
- Con el ID de proceso único obtenido por el proceso secundario, no coincide con el ID de ningún grupo de procesos existente.
Para elaborar sobre el fork(), tomemos un ejemplo que aclara el concepto fork().
$ sudo vim fork.c
Aquí está el código para copiar/pegar:
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/types.h> int main(int argc, char **argv) { pid_t pid; pid = fork(); if(pid==0) { printf("It is the child process and pid is %d\n",getpid()); exit(0); } else if(pid > 0) { printf("It is the parent process and pid is %d\n",getpid()); } else { printf("Error while forking\n"); exit(EXIT_FAILURE); } return 0; }
Producción:
$make fork
Y al ejecutar el script, obtenemos el resultado como se muestra a continuación.
$ ./fork
exec()
El exec() es una llamada al sistema que se ejecuta al reemplazar la imagen del proceso actual con la nueva imagen del proceso. Sin embargo, el proceso original permanece como un nuevo proceso, pero el nuevo proceso reemplaza los datos principales, los datos de la pila, etc. Ejecuta el programa desde el punto de entrada al cargar el programa en el espacio de proceso actual.
Para elaborar más, tomemos un ejemplo como se muestra a continuación.
$ sudo vim exec.c
Y aquí está el código:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> main(void) { pid_t pid = 0; int status; pid = fork(); if (pid == 0) { printf("I am the child."); execl("/bin/ls", "ls", "-l", "/home/ubuntu/", (char *) 0); perror("In exec(): "); } if (pid > 0) { printf("I am the parent, and the child is %d.\n", pid); pid = wait(&status); printf("End of process %d: ", pid); if (WIFEXITED(status)) { printf("The process ended with exit(%d).\n", WEXITSTATUS(status)); } if (WIFSIGNALED(status)) { printf("The process ended with kill -%d.\n", WTERMSIG(status)); } } if (pid < 0) { perror("In fork():"); } exit(0); }
Producción:
$ make exec
Y al ejecutar el script, obtenemos el resultado como se muestra a continuación.
$ ./exec
wait()
Como en el caso de una bifurcación, los procesos secundarios se crean y se ejecutan, pero el proceso principal se suspende hasta que se ejecuta el proceso secundario. En este caso, una llamada al sistema wait() se activa automáticamente debido a la suspensión del proceso padre. Después de que el proceso secundario termina la ejecución, el proceso principal recupera el control nuevamente.
Para dar más detalles sobre wait(), tomemos un ejemplo que aclara la llamada al sistema wait().
$ sudo vim wait.c
Un aquí es el ejemplo de código:
#include<stdio.h> // printf() #include<stdlib.h> // exit() #include<sys/types.h> // pid_t #include<sys/wait.h> // wait() #include<unistd.h> // fork int main(int argc, char **argv) { pid_t pid; pid = fork(); if(pid==0) { printf("It is the child process and pid is %d\n",getpid()); int i=0; for(i=0;i<8;i++) { printf("%d\n",i); } exit(0); } else if(pid > 0) { printf("It is the parent process and pid is %d\n",getpid()); int status; wait(&status); printf("Child is reaped\n"); } else { printf("Error in forking..\n"); exit(EXIT_FAILURE); } return 0; }
Producción:
$ make wait
Y al ejecutar el script, obtenemos el resultado como se muestra a continuación.
$ ./wait
exit()
exit() es una función de este tipo o una de las llamadas al sistema que se utiliza para terminar el proceso. Esta llamada al sistema define que la ejecución del subproceso se completa, especialmente en el caso de un entorno de subprocesos múltiples. Para referencia futura, se captura el estado del proceso.
Después del uso de la llamada al sistema exit(), el sistema operativo recupera todos los recursos utilizados en el proceso y luego finaliza el proceso. La llamada al sistema Exit() es equivalente a exit().
Sinopsis
#include <unistd.h> void _exit(int status); #include <stdlib.h> void _Exit(int status);
Puede ver el uso de la función exit() en los ejemplos anteriores de fork(), wait(). El uso de la llamada al sistema exit() se realiza para finalizar el proceso.
Conclusión
En este artículo, aprendimos las llamadas al sistema fork(), exec(), wait() y exit() en detalle con algunos ejemplos. Para obtener más detalles, intente ejecutar los programas utilizando esas llamadas al sistema y vea el resultado. ¡Gracias!