¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Procesos en C: fork, exec y wait

Después de esta lección: Puedo explicar cómo un proceso crea a otro con fork, exec y wait.

Lección de 60 min12 comandosLaboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

Qué pasa al escribir una orden

Un proceso es un programa en ejecución, y en Linux cada proceso nace de otro mediante la llamada fork. La terminal lo hace cada vez que escribes una orden: crea un proceso hijo y espera a que acabe.

En el aula de informática, diez estudiantes escriben ls a la vez. Cada terminal crea su propio hijo, y ninguno estorba a los demás.

Al final de esta lección

Nota

Puedo explicar cómo un proceso crea a otro con fork, exec y wait.

Palabras clave

PalabraQué significaEjemplo
Procesoun programa en ejecución, con su memoria propia
forkla llamada que crea una copia del procesofork()
execla llamada que cambia el programa del proceso por otro
waitla llamada con la que un padre espera a su hijo
Estado de salidael número con que acaba un proceso; 0 significa bien
Zombiun hijo que ha acabado y al que el padre aún no ha esperado

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

fork copia el proceso que lo llama

fork() crea un proceso nuevo, el hijo, que es una copia exacta del padre: su memoria, sus variables y la línea por la que va.

Tras el fork, hay dos procesos que ejecutan el mismo código. Se distinguen por el valor que devuelve la llamada. El hijo recibe 0, y el padre recibe el número del hijo.

Qué devuelve fork en cada proceso

ProcesoQué devuelve forkQué suele hacer
El hijo0hace su trabajo y sale con exit
El padreun número mayor que 0, el del hijoespera al hijo con wait
Cualquiera, si no hay sitio-1avisa del error

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Entra en la carpeta de trabajo

Comando
mkdir -p ~/c && cd ~/c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe fork.c: un padre y un hijo

Comando
printf '#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/wait.h>\nint main(void) {\n  pid_t hijo = fork();\n  if (hijo == 0) {\n    printf("hijo: termino con 3\\n");\n    exit(3);\n  }\n  int estado;\n  waitpid(hijo, &estado, 0);\n  printf("padre: el hijo termino con %%d\\n", WEXITSTATUS(estado));\n  return 0;\n}\n' > fork.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila fork.c con avisos

Comando
gcc -Wall -o fork fork.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta fork

Comando
./fork

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

exec cambia el programa, no el proceso

Un hijo copiado del padre sigue ejecutando el mismo programa. Para lanzar otro, el hijo llama a exec.

exec sustituye el programa del proceso por uno nuevo, desde su primera línea. El proceso es el mismo, con el mismo número. Si exec funciona, no vuelve: el código que había detrás ya no existe.

Un proceso que sale con estado 7

Comando
sh -c 'exit 7'; echo $?

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

exec en sh: la segunda orden no corre

Comando
sh -c 'exec echo hola desde exec; echo no se llega aqui'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Escribe ex.c, que se cambia por echo

Comando
printf '#include <stdio.h>\n#include <unistd.h>\nint main(void) {\n  printf("antes de exec\\n");\n  fflush(stdout);\n  execlp("echo", "echo", "ahora soy echo", (char *)NULL);\n  printf("no se llega aqui\\n");\n  return 1;\n}\n' > ex.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila ex.c

Comando
gcc -Wall -o ex ex.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta ex

Comando
./ex

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Cada proceso tiene un padre: un árbol

El diagrama muestra la terminal, el proceso 1, con dos hijos: ls y una sh. Esa sh tiene un hijo, gcc.

Cada fork añade una rama. Cuando un hijo acaba, el padre debe esperarlo con wait. Hasta entonces el hijo es un zombi: ya no corre, pero su estado de salida sigue guardado.

Lee una traza de procesos

Código: python
# Un registro de procesos: cada línea dice quién creó a quién.
trace = ["fork 1 2", "fork 1 3", "fork 3 4"]
for line in trace:
    words = line.split(" ")
    print("el proceso", words[1], "creó al proceso", words[2])
# Añade "fork 4 5" a la lista: ¿a qué profundidad queda el proceso 5?

Escribe copia.c: el hijo cambia una variable

Comando
printf '#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <sys/wait.h>\nint main(void) {\n  int x = 10;\n  pid_t hijo = fork();\n  if (hijo == 0) { x = 99; exit(0); }\n  wait(NULL);\n  printf("x en el padre: %%d\\n", x);\n  return 0;\n}\n' > copia.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Compila copia.c

Comando
gcc -Wall -o copia copia.c

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Ejecuta copia: x sigue valiendo 10

Comando
./copia

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que fork lanza un programa nuevo. No: crea una copia del proceso que lo llama. Para ejecutar otro programa, el hijo llama después a exec. Y como la memoria se copia, lo que el hijo cambia no llega al padre.

Sobre este laboratorio y su código: Laboratorio de compiladores y arquitectura: gcc, flex, bison y RISC-V en una VM

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.