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
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Proceso | un programa en ejecución, con su memoria propia | |
| fork | la llamada que crea una copia del proceso | fork() |
| exec | la llamada que cambia el programa del proceso por otro | |
| wait | la llamada con la que un padre espera a su hijo | |
| Estado de salida | el número con que acaba un proceso; 0 significa bien | |
| Zombi | un 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
| Proceso | Qué devuelve fork | Qué suele hacer |
|---|---|---|
| El hijo | 0 | hace su trabajo y sale con exit |
| El padre | un número mayor que 0, el del hijo | espera al hijo con wait |
| Cualquiera, si no hay sitio | -1 | avisa del error |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Entra en la carpeta de trabajo
mkdir -p ~/c && cd ~/cSe 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
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.cSe 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
gcc -Wall -o fork fork.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta fork
./forkSe 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
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
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
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.cSe 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
gcc -Wall -o ex ex.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta ex
./exSe 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
# 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
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.cSe 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
gcc -Wall -o copia copia.cSe 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
./copiaSe 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.