Procesos en Linux con C — ejercicios con cocineros que se multiplican, búferes que se duplican y códigos de salida que mienten (y la cocina que contrató a siete cocineros)
Quería que mi jefe de cocina contratara a tres cocineros, así que metí el fork() en un bucle de tres vueltas. Ejecuté y la terminal se llenó: «Cocinero 1», «Cocinero 2», «Cocinero 2», «Cocinero 3», «Cocinero 3», «Cocinero 3», «Cocinero 3». Siete mensajes para tres cocineros. Conté los PID: todos distintos. No se estaba repitiendo la salida: de verdad había más procesos de los que yo había pedido. El culpable era una sola línea que faltaba, y es el primer fragmento de «¿Qué falla aquí?» de hoy.
Estos ejercicios cierran el tema de procesos. Si vienes directamente, repasa antes la teoría y la práctica de procesos en C, porque aquí ya no te doy el código hecho.
Tabla de Contenidos
Cómo usar este artículo
- Lee el enunciado y la salida esperada de cada nivel, y escribe tu programa sin mirar las soluciones.
- Si te atascas más de diez minutos, abre la pista 🔵 de ese nivel, y solo esa.
- En «¿Qué falla aquí?», predice la salida antes de leer la explicación. Equivocarte en la predicción es justo lo que hace que se te quede.
- Compara tu solución con la mía y, sobre todo, lee el «Por qué está hecho así»: ahí están las decisiones que separan un programa que funciona de uno que funciona por casualidad. Al final tienes el chuletario para repasar.
En todos los ejercicios los PID de tu salida serán distintos de los míos y cambiarán en cada ejecución. Lo que tiene que cuadrar es la relación entre ellos (el PID que recibe el padre es el del hijo, etc.) y el resto de los datos.
Nivel 1 (básico): el cocinero se presenta
Escribe un programa en el que el jefe crea un cocinero con fork(). El cocinero imprime su PID y el de su jefe y termina. El jefe espera a que el cocinero acabe y después imprime su propio PID y el valor que le devolvió fork(). Comprueba en la salida que los números cuadran entre los dos mensajes. Controla también el caso en que fork() falle.
Salida esperada (con tus PID):
Cocinero: mi PID es 444 y mi jefe es 443 Jefe: mi PID es 443 y fork() me devolvió 444
El jefe tiene que imprimir después del cocinero siempre, no solo cuando el planificador quiera. ¿Qué llamada lo garantiza? Y recuerda que el PID del hijo, visto desde el padre, es justo lo que devuelve fork().
Nivel 2 (intermedio): ¿cuántos platos habéis hecho?
Dos cocineros preparan 4 y 7 platos. Cada uno le devuelve al jefe su número de platos (sin variables compartidas: ya sabes que no funcionan). El jefe espera a los dos en el orden en que terminen, imprime cuántos platos hizo cada cocinero (identificándolo por su número, 1 o 2) y el total. Es el ejercicio 6.5 de la práctica con números fijos en lugar de aleatorios, para que la salida se pueda comprobar.
Salida esperada (el orden de los dos cocineros puede cambiar entre ejecuciones):
Cocinero 2: 7 platos Cocinero 1: 4 platos Total de platos: 11
El número viaja con exit(). Para leerlo, wait(&estado) y WEXITSTATUS. Y para saber de qué cocinero es, compara el PID que devuelve wait con los que guardaste al crear a los hijos.
Nivel 3 (desafío): las estadísticas del servicio
Cinco cocineros tardan 4, 8, 2, 10 y 6 minutos. Cada minuto se simula con 0,1 segundos de espera (usleep), así que el que menos tarda termina antes. Cada cocinero devuelve su tiempo al jefe. El jefe recoge a los cinco en el orden en que llegan, imprime quién llega y con qué tiempo, y al final calcula el tiempo medio y la desviación típica (la de la población: raíz cuadrada de la media de los cuadrados de las diferencias con la media), con dos decimales. Es el ejercicio 6.6 de la práctica con tiempos fijos.
Salida esperada:
Llega el cocinero 3: 2 min Llega el cocinero 1: 4 min Llega el cocinero 5: 6 min Llega el cocinero 2: 8 min Llega el cocinero 4: 10 min Tiempo medio: 6.00 min Desviación típica: 2.83 min
Guarda cada tiempo en un array según llega. Para la desviación necesitas sqrt de <math.h>, y al compilar hay que añadir -lm al final: gcc -Wall -std=c11 nivel3.c -o nivel3 -lm. Con -std=c11, usleep está escondida: pon #define _DEFAULT_SOURCE antes de los #include.
¿Qué falla aquí?
Tres fragmentos, tres errores que me encontré (o que vi en compañeros) haciendo la práctica. Lee cada uno, predice qué imprime y luego sigue leyendo.
Fragmento 1: el bucle que contrata de más
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
for (int i = 0; i < 3; i++) {
pid_t pid = fork();
if (pid == 0) {
printf("Cocinero %d en marcha (PID %d)\n", i + 1, getpid());
}
}
/* cada proceso espera a todos sus hijos antes de terminar */
while (wait(NULL) > 0) {
}
return 0;
}
Predice: ¿cuántas líneas «Cocinero … en marcha» salen?
Salida real:
Cocinero 1 en marcha (PID 394) Cocinero 2 en marcha (PID 395) Cocinero 2 en marcha (PID 397) Cocinero 3 en marcha (PID 398) Cocinero 3 en marcha (PID 396) Cocinero 3 en marcha (PID 399) Cocinero 3 en marcha (PID 400)
Siete, y con siete PID distintos. El cocinero que nace en la vuelta i = 0 imprime su mensaje… y como no hay ningún exit(), sale del if y sigue en el bucle, con su propia copia de i. En la vuelta i = 1 hace su propio fork(), igual que el jefe. Cada proceso vivo al empezar una vuelta crea un hijo nuevo, así que el número de procesos se duplica en cada vuelta: 1 → 2 → 4 → 8. Y cada hijo nuevo imprime una línea con la vuelta en la que nació.
La cuenta, vuelta a vuelta:
Vuelta i | Procesos antes del fork | Hijos nuevos (cada uno imprime «Cocinero i+1») | Procesos después |
|---|---|---|---|
| 0 | 1 | 1 | 2 |
| 1 | 2 | 2 | 4 |
| 2 | 4 | 4 | 8 |
1 + 2 + 4 = 7 líneas y 8 procesos en total (contando al jefe, que no imprime). El while (wait(NULL) > 0) hace que cada proceso espere a todos sus hijos, por eso nadie termina antes de tiempo, pero el orden de las líneas lo decide el planificador.
Con 10 vueltas serían 2¹⁰ = 1024 procesos y 1023 líneas. Con un bucle infinito, el sistema se queda sin procesos libres: es lo que se conoce como fork bomb. Un exit() que falta no es un detalle.
El arreglo es una línea: el cocinero termina dentro de su rama.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
for (int i = 0; i < 3; i++) {
pid_t pid = fork();
if (pid == 0) {
printf("Cocinero %d en marcha (PID %d)\n", i + 1, getpid());
exit(0); /* el cocinero termina aquí y no vuelve al bucle */
}
}
while (wait(NULL) > 0) {
}
return 0;
}
Salida real del arreglo:
Cocinero 2 en marcha (PID 408) Cocinero 1 en marcha (PID 407) Cocinero 3 en marcha (PID 409)
Tres líneas, tres cocineros. (Fíjate en que el 2 habló antes que el 1: los tres trabajan a la vez y el orden no está garantizado.)
Haz que todo hijo termine en la última línea de su rama con exit(), aunque creas que no hace falta. Es la forma más simple de no tener nunca un hijo «suelto» ejecutando código del padre.
Fragmento 2: la comanda que se recibe dos veces
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
printf("Comanda recibida. ");
pid_t pid = fork();
if (pid == 0) {
printf("Cocinero: a los fogones\n");
exit(0);
}
wait(NULL);
printf("Jefe: plato servido\n");
return 0;
}
Predice: «Comanda recibida.» se imprime antes del fork(), una sola vez… ¿no?
Salida real:
Comanda recibida. Cocinero: a los fogones Comanda recibida. Jefe: plato servido
Sale dos veces. Y no porque el hijo vuelva a ejecutar el printf: el hijo nace después de esa línea. La clave es que printf no escribe directamente en la pantalla, sino en un búfer en memoria que se vacía cuando llega un \n (en una terminal), cuando se llena o cuando el proceso termina. «Comanda recibida. » no tiene \n, así que en el momento del fork() sigue en el búfer… y el búfer es memoria del proceso, así que se copia al hijo. Cada uno añade su línea y, al terminar, cada uno vacía su búfer completo.
Este fallo es aún más traicionero si rediriges la salida a un fichero (./buffer > log.txt). Ahí printf no vacía el búfer ni siquiera con \n, y cualquier mensaje anterior al fork() puede aparecer duplicado aunque termine en salto de línea.
El arreglo es vaciar el búfer antes de crear al hijo:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
printf("Comanda recibida. ");
fflush(stdout); /* vacía el búfer ANTES de fork: no queda nada que copiar */
pid_t pid = fork();
if (pid == 0) {
printf("Cocinero: a los fogones\n");
exit(0);
}
wait(NULL);
printf("Jefe: plato servido\n");
return 0;
}
Salida real del arreglo:
Comanda recibida. Cocinero: a los fogones Jefe: plato servido
Ahora «Comanda recibida. » sale una vez: el fflush(stdout) la escribió en la pantalla antes del fork() y el hijo heredó un búfer vacío. El hijo continúa en la misma línea porque no hubo salto de línea, que es justo lo que pedía el código.
La regla práctica: fflush(stdout) justo antes de cada fork() si has impreso algo. No cuesta nada y te ahorra este misterio.
Fragmento 3: el cocinero más lento del mundo
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
pid_t pid = fork();
if (pid == 0) {
exit(7); /* el cocinero ha tardado 7 minutos */
}
int estado;
wait(&estado);
printf("El cocinero ha tardado %d minutos\n", estado);
return 0;
}
Predice: el cocinero hizo exit(7). ¿Cuántos minutos imprime el jefe?
Salida real:
El cocinero ha tardado 1792 minutos
1792 minutos: casi 30 horas para un plato. El 7 está ahí, pero escondido: wait no guarda el código de salida tal cual, sino en el segundo byte del entero estado, es decir, multiplicado por 256. 7 × 256 = 1792. El byte bajo se reserva para la señal que podría haber terminado al proceso.
Cuando un resultado te sale múltiplo de 256 (256, 512, 1792…), casi seguro que estás imprimiendo estado en lugar de WEXITSTATUS(estado).
El arreglo usa las macros de <sys/wait.h>:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
pid_t pid = fork();
if (pid == 0) {
exit(7); /* el cocinero ha tardado 7 minutos */
}
int estado;
wait(&estado);
if (WIFEXITED(estado)) {
printf("El cocinero ha tardado %d minutos\n", WEXITSTATUS(estado));
}
return 0;
}
Salida real del arreglo:
El cocinero ha tardado 7 minutos
WIFEXITED confirma que el cocinero terminó con exit y WEXITSTATUS saca el 7 del segundo byte.
Si quieres comprobar el desplazamiento con tus propios ojos, imprime estado >> 8: da el mismo 7. Pero en tu código usa la macro, que es lo portable.
Soluciones
Solución del nivel 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
}
if (pid == 0) {
/* el cocinero se presenta */
printf("Cocinero: mi PID es %d y mi jefe es %d\n", getpid(), getppid());
exit(0);
}
/* el jefe espera para que su mensaje salga siempre después */
wait(NULL);
printf("Jefe: mi PID es %d y fork() me devolvió %d\n", getpid(), pid);
return 0;
}
Salida real:
Cocinero: mi PID es 444 y mi jefe es 443 Jefe: mi PID es 443 y fork() me devolvió 444
Comprobación: el cocinero dice que su PID es 444, y el jefe dice que fork() le devolvió 444. El cocinero dice que su jefe es 443, y el jefe dice que su PID es 443. Los dos pares cuadran, que es justo lo que pedía el enunciado.
Por qué está hecho así:
- **
wait(NULL)antes del mensaje del jefe.** Sin él, el orden de las dos líneas dependería del planificador. PasamosNULLporque no nos interesa cómo terminó el cocinero, solo que haya terminado. - **
exit(0)al final de la rama del hijo.** Aunque después no haya ningún bucle, si el cocinero no saliera ejecutaría elwait(que le devolvería -1 al no tener hijos) y elprintfdel jefe: verías dos líneas «Jefe» y la segunda diría que fork() devolvió 0. - **
return 1sipid < 0.** Si no hay hijo, no tiene sentido seguir; el 1 avisa a quien lanzó el programa de que algo ha ido mal.
Solución del nivel 2
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
int platos[2] = {4, 7}; /* platos que prepara cada cocinero */
pid_t pids[2];
for (int i = 0; i < 2; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
return 1;
}
if (pids[i] == 0) {
/* cada cocinero devuelve su número de platos como código de salida */
exit(platos[i]);
}
}
int total = 0;
for (int k = 0; k < 2; k++) {
int estado;
pid_t fin = wait(&estado); /* el primero que acabe, sea cual sea */
if (WIFEXITED(estado)) {
int p = WEXITSTATUS(estado);
/* averiguamos qué cocinero era comparando el PID */
int num = (fin == pids[0]) ? 1 : 2;
printf("Cocinero %d: %d platos\n", num, p);
total += p;
}
}
printf("Total de platos: %d\n", total);
return 0;
}
Salida real:
Cocinero 2: 7 platos Cocinero 1: 4 platos Total de platos: 11
En mi ejecución terminó antes el cocinero 2, y el programa lo identificó bien. Total: 4 + 7 = 11 platos, el mismo resultado sea cual sea el orden de llegada.
La recogida, con lupa:
int total = 0;
for (int k = 0; k < 2; k++) {
int estado;
pid_t fin = wait(&estado); /* el primero que acabe, sea cual sea */
if (WIFEXITED(estado)) {
int p = WEXITSTATUS(estado);
/* averiguamos qué cocinero era comparando el PID */
int num = (fin == pids[0]) ? 1 : 2;
printf("Cocinero %d: %d platos\n", num, p);
total += p;
}
}
printf("Total de platos: %d\n", total);
Vuelta k | wait devuelve | ¿Es pids[0]? | num | WEXITSTATUS | total |
|---|---|---|---|---|---|
| 0 | PID del cocinero 2 | no | 2 | 7 | 7 |
| 1 | PID del cocinero 1 | sí | 1 | 4 | 11 |
Por qué está hecho así:
- **
waity nowaitpid**, porque el enunciado pide recoger en el orden en que terminan. Conwaitpid(pids[k], …)siempre saldría primero el cocinero 1. - **
(fin == pids[0]) ? 1 : 2**: con dos hijos basta una comparación. Con más, harías el bucle de búsqueda del programa 1 de la práctica. - **
if (WIFEXITED(estado))** antes de leer el código: si un cocinero muriera por una señal, no sumamos un número sin sentido. - 4 y 7 caben en un byte. Si un cocinero preparara 300 platos, esta solución no serviría (llegarían 44).
Solución del nivel 3
#define _DEFAULT_SOURCE /* pide a glibc usleep(), que -std=c11 esconde */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <math.h>
#define N 5
int main(void) {
int minutos[N] = {4, 8, 2, 10, 6};
pid_t pids[N];
for (int i = 0; i < N; i++) {
pids[i] = fork();
if (pids[i] < 0) {
perror("fork");
return 1;
}
if (pids[i] == 0) {
/* cada minuto simulado dura 0,1 s: el que menos tarda acaba antes */
usleep(minutos[i] * 100000);
exit(minutos[i]);
}
}
int recogidos[N]; /* los tiempos, en el orden en que llegan */
for (int k = 0; k < N; k++) {
int estado;
pid_t fin = wait(&estado);
int num = 0;
for (int i = 0; i < N; i++) {
if (pids[i] == fin) {
num = i + 1;
}
}
recogidos[k] = WEXITSTATUS(estado);
printf("Llega el cocinero %d: %d min\n", num, recogidos[k]);
}
/* media: suma entre N, en double para no perder decimales */
double suma = 0;
for (int k = 0; k < N; k++) {
suma += recogidos[k];
}
double media = suma / N;
/* desviación típica: raíz de la media de los cuadrados de (x - media) */
double cuadrados = 0;
for (int k = 0; k < N; k++) {
cuadrados += (recogidos[k] - media) * (recogidos[k] - media);
}
double desviacion = sqrt(cuadrados / N);
printf("Tiempo medio: %.2f min\n", media);
printf("Desviación típica: %.2f min\n", desviacion);
return 0;
}
Salida real:
Llega el cocinero 3: 2 min Llega el cocinero 1: 4 min Llega el cocinero 5: 6 min Llega el cocinero 2: 8 min Llega el cocinero 4: 10 min Tiempo medio: 6.00 min Desviación típica: 2.83 min
Los cocineros llegan en el orden de sus tiempos: el 3 (2 min, 0,2 s), el 1 (4 min), el 5 (6 min), el 2 (8 min) y el 4 (10 min). Todo el programa tarda lo que el más lento, 1 segundo, porque los cinco esperan a la vez.
Ahora las cuentas a mano. La media: (2 + 4 + 6 + 8 + 10) / 5 = 30 / 5 = 6,00. Para la desviación típica:
Tiempo x | x − media | (x − media)² |
|---|---|---|
| 2 | −4 | 16 |
| 4 | −2 | 4 |
| 6 | 0 | 0 |
| 8 | 2 | 4 |
| 10 | 4 | 16 |
| Suma | 40 |
40 / 5 = 8, y la raíz de 8 es 2,828…, que con dos decimales es 2,83. Cuadra con la salida.
La parte de procesos es la recogida:
int recogidos[N]; /* los tiempos, en el orden en que llegan */
for (int k = 0; k < N; k++) {
int estado;
pid_t fin = wait(&estado);
int num = 0;
for (int i = 0; i < N; i++) {
if (pids[i] == fin) {
num = i + 1;
}
}
recogidos[k] = WEXITSTATUS(estado);
printf("Llega el cocinero %d: %d min\n", num, recogidos[k]);
}
Por qué está hecho así:
- **
usleep(minutos[i] * 100000)**:usleeprecibe microsegundos, así que 4 minutos son 400 000 µs = 0,4 s. Consleep(segundos enteros) el programa tardaría 10 segundos. - **
#define _DEFAULT_SOURCEen la primera línea**:-std=c11pide C estándar «puro» y glibc esconde las funciones que no lo son, comousleep. Esa macro, definida antes de cualquier#include, le pide que las muestre (lo viste en el tema del preprocesador: las macros cambian lo que ve el compilador). - **
recogidos[k]en el orden de llegada**: la media y la desviación no dependen del orden, así que no hace falta reordenar; pero así la salida cuenta la historia real del servicio. - **
doubleparasuma,mediaycuadrados**: con enteros, la media de 4, 8 y 3 (15 / 3) daría bien, pero la de 4 y 7 daría 5 en lugar de 5,5. - **
-lmal compilar**:sqrtestá en la biblioteca matemática, que gcc no enlaza por defecto. Sin-lmverásundefined reference to 'sqrt'.
Míralo paso a paso en Python Tutor
Python Tutor ejecuta el código línea a línea y dibuja las variables (elige C en el desplegable). Como no puede crear procesos, vamos a ver la parte del fragmento 3 que se puede simular: qué entero recibe el padre según el número que el hijo pasa a exit(), incluido el caso que se sale de rango.
#include <stdio.h>
/* qué le llega al padre según el número que el hijo pasa a exit() */
int main(void) {
int codigos[3] = {7, 255, 300};
for (int i = 0; i < 3; i++) {
/* el sistema solo guarda el byte bajo (0-255), desplazado 8 bits */
int estado = (codigos[i] & 0xFF) << 8;
int recibido = (estado >> 8) & 0xFF; /* lo que daría WEXITSTATUS */
printf("exit(%d): estado=%d, WEXITSTATUS=%d\n", codigos[i], estado, recibido);
}
return 0;
}
Salida:
exit(7): estado=1792, WEXITSTATUS=7 exit(255): estado=65280, WEXITSTATUS=255 exit(300): estado=11264, WEXITSTATUS=44
Qué observar paso a paso:
- En la primera vuelta,
estadovale 1792: el mismo número del fragmento 3.recibidovuelve a ser 7. - En la segunda vuelta, 255 da
estado65280 (255 × 256) yrecibido255: es el máximo que cabe. - En la tercera vuelta, fíjate en
codigos[i] & 0xFF: de 300 solo se queda 44 (300 − 256). Por esoexit(300)llega al padre como 44. - Compara las dos columnas de cada línea:
estadosiempre esrecibido× 256. Eso es todo lo que hace la macroWEXITSTATUSal revés.
Chuletario: procesos en C
Lo esencial en 30 segundos
fork()crea una copia del proceso: devuelve 0 al hijo, el PID del hijo al padre y -1 si falla.- El hijo termina siempre con
exit()dentro de su rama. - La memoria se copia: lo que cambia el hijo no lo ve el padre.
execsustituye el programa (mismo PID) y no vuelve si funciona. Para lanzar otro programa:fork+execen el hijo.wait(&estado)espera al primero que termine;waitpid(pid, &estado, 0), a uno concreto.- El código de
exit(n)se lee conWEXITSTATUS(estado)y va de 0 a 255. fflush(stdout)antes defork()si has impreso algo.
Quiero… / Código / Resultado
| Quiero… | Código | Resultado |
|---|---|---|
| Mi PID y el de mi padre | getpid(), getppid() | Dos pid_t (enteros) |
| Crear un hijo | pid_t pid = fork(); | Dos procesos desde aquí |
| Saber si soy el hijo | if (pid == 0) | Solo el hijo entra |
| Esperar a cualquier hijo | pid_t fin = wait(&estado); | PID del que acabó, -1 si no quedan |
| Esperar a uno concreto | waitpid(pid, &estado, 0); | Bloquea hasta que acabe ese |
| Devolver un número | exit(7); | El padre lee 7 con WEXITSTATUS |
Saber si acabó con exit | WIFEXITED(estado) | Verdadero o falso |
| Saber qué señal lo mató | WIFSIGNALED(estado), WTERMSIG(estado) | Por ejemplo, 6 con abort() |
| Ejecutar otro programa (lista) | execlp("date", "date", NULL); | Se convierte en date |
| Ejecutar otro programa (array) | char *a[] = {"ls", "-l", NULL}; execvp(a[0], a); | Se convierte en ls -l |
Avisar si exec falla | perror(a[0]); exit(127); | ls: No such file or directory, código 127 |
Plantillas de los ejercicios
Crear N hijos que trabajan a la vez y recogerlos:
pid_t pids[N];
for (int i = 0; i < N; i++) {
pids[i] = fork();
if (pids[i] < 0) { perror("fork"); exit(1); }
if (pids[i] == 0) {
/* trabajo del hijo i */
exit(resultado); /* 0..255 */
}
}
for (int k = 0; k < N; k++) {
int estado;
pid_t fin = wait(&estado); /* el primero que acabe */
if (WIFEXITED(estado)) {
int r = WEXITSTATUS(estado);
/* buscar fin en pids[] para saber quién era */
}
}
Que una orden se ejecute como en la shell y saber cómo ha ido:
pid_t pid = fork();
if (pid == 0) {
execvp(argv[0], argv); /* argv termina en NULL */
perror(argv[0]);
exit(127);
}
int estado;
waitpid(pid, &estado, 0);
int codigo = WIFEXITED(estado) ? WEXITSTATUS(estado) : -1;
Con estas plantillas salen los tres niveles: el 1 con N = 1, el 2 con N = 2 y la suma de los códigos (11 platos) y el 3 con N = 5, usleep en el hijo y la media 6,00 y la desviación 2,83 al final.
Síntoma / Causa probable
| Síntoma | Causa probable |
|---|---|
| Salen más mensajes de hijos de los que creaste | Falta exit() en la rama del hijo dentro de un bucle |
Un texto anterior al fork() sale dos veces | Quedaba en el búfer: falta \n o fflush(stdout) |
| El código de salida sale 256, 512, 1792… | Imprimes estado en vez de WEXITSTATUS(estado) |
exit(300) llega como 44 | El código solo tiene un byte (0-255) |
| El padre no ve lo que cambió el hijo | La memoria se copia, no se comparte |
No sale nada de lo que hay después del exec | Normal: exec funcionó y sustituyó el programa |
| El prompt aparece antes que los mensajes del hijo | El padre no hizo wait() |
unknown type name 'pid_t' | Falta <sys/types.h> (o <sys/wait.h>) con -std=c11 |
implicit declaration of function 'usleep' | Falta #define _DEFAULT_SOURCE antes de los #include |
undefined reference to 'sqrt' | Falta -lm al final de la orden de gcc |
Antes de entregar, pregúntate
- ¿Todos mis hijos terminan con
exit()dentro de su rama? - ¿Hago un
wait(owaitpid) por cada hijo que creo? - ¿Leo el código de salida con
WIFEXITEDyWEXITSTATUS, y cabe en un byte? - ¿He explicado en la memoria que los PID y el orden de los mensajes cambian en cada ejecución?
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →
