Desarrollo y Optimización de Procesos C con POSIX y Makefile para Minishell
Corrección y Optimización de Código C para Gestión de Procesos POSIX
A continuación, se presenta una revisión y corrección detallada de un documento técnico relacionado con la programación de sistemas, específicamente en el ámbito de la gestión de procesos POSIX en C y la implementación de un Makefile para un proyecto de minishell.
Código Fuente Inicial y Jerarquía de Procesos
El siguiente fragmento de código C establece una jerarquía de procesos utilizando la llamada al sistema fork().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "manejo_jerarquia.h"
void mostrar_info()
{
printf("Parentesco: %d - %d\n", getpid(), getppid());
exit(0);
}
int main() {
int i, ident;
printf("Proceso: %d\n", getpid());
for (i = 0; i < 3; i++) {
ident = fork();
if (ident == 0)
mostrar_info();
}
escribir_jerarquia();
while (1) {}; /* Bucle de espera activa */
}
La jerarquía de procesos descrita es: P1 (P2(P21(P211)P22)P3(P31)P4). Todos los procesos se crean al ejecutarse la línea 20 del código (ident = fork();).
Cuestiones y Soluciones Detalladas
c) Finalización del Código y Gestión de Procesos Zombie
Pregunta: ¿Cree que la forma de finalizar el código inicial de jerarquia.c (sentencia de la línea 26) es la más adecuada? En caso negativo, indique el porqué y cómo lo modificaría (incluya el código). Razone claramente la respuesta.
Respuesta: La forma de finalizar el código inicial no es la adecuada, dado que:
- El proceso padre resultante de ejecutar el código proporcionado no acabará nunca. La sentencia de la línea 26 (
while (1) {};) es un bucle cuya condición siempre se cumple sin hacer nada en cada iteración (conocido como espera activa o busy-waiting). - El proceso padre no elimina el bloque de control de procesos de sus procesos hijos cuando estos finalizan, ya que no invoca al servicio POSIX
wait()(owaitpid()) por cada uno de ellos. Por lo tanto, los tres procesos hijos quedan en estado zombie.
Se solucionan los problemas anteriores sustituyendo la línea 26, por ejemplo, con tres sentencias wait(NULL); consecutivas o bien por la siguiente estructura de bucle:
while (waitpid(-1, NULL, 0) > 0);
Esta última opción espera a cualquier hijo que termine, liberando sus recursos.
d) Secuencia de Salida y Sincronización de Procesos
Pregunta: Suponiendo la siguiente información:
- El PID de la shell que lanza
jerarquia.ces 1767. - No hay ningún otro proceso que haya creado nuevos procesos desde que arrancó el intérprete de órdenes hasta finalizar esta prueba.
Muestre qué saldría por pantalla como resultado de ejecutar el código inicial de jerarquia.c. ¿Con este código se garantiza que la secuencia en la que se mostrará la información de los procesos coincidirá siempre con el orden en el que estos fueron creados? En caso negativo, indique detalladamente, si existe, cómo lo lograría. Razone claramente la respuesta.
Respuesta: Lo que se muestra por pantalla son las líneas resultantes de ejecutar la línea 9 en la invocación a la función mostrar_info() por cada proceso hijo. El resultado es el que se muestra a continuación, sin poder garantizar que el orden en el que se visualizarán las líneas sea el mostrado. Esto último se debe a que no se sabe la velocidad de ejecución ni cuándo comenzarán a ejecutarse cada proceso hijo.
Parentesco: 1769 - 1768
Parentesco: 1770 - 1768
Parentesco: 1771 - 1768
Para lograr que el orden en el que se imprime el parentesco sea el mismo que el orden en que son creados los tres procesos hijos, hay que esperar su finalización antes de crear el siguiente. Para ello, se invoca al servicio POSIX wait(NULL); entre las líneas 22 y 23 del código, tras la sentencia if, dentro del bucle for. La modificación sería la siguiente:
for (i = 0; i < 3; i++) {
ident = fork();
if (ident == 0) {
mostrar_info();
}
wait(NULL); // Espera la finalización del hijo antes de la siguiente iteración
}
e) Implementación de un Makefile
Pregunta: (2 puntos) Implemente un archivo Makefile de tal manera que el ejecutable generado a partir de este proyecto se llame examenssoo.
Respuesta:
.PHONY: clean all
all: examenssoo
examenssoo: jerarquia.o manejo_jerarquia.o libvis.a libmisc.a
gcc -Wall jerarquia.o manejo_jerarquia.o -Llib -lmisc -lvis -o examenssoo
jerarquia.o: jerarquia.c manejo_jerarquia.h
gcc -c -o jerarquia.o jerarquia.c
manejo_jerarquia.o: manejo_jerarquia.c manejo_jerarquia.h
gcc -c -o manejo_jerarquia.o manejo_jerarquia.c
clean:
rm -f *.o examenssoo
f) Ejecución en una Minishell y Servicios POSIX
Pregunta: (3 puntos) Considere que el Makefile anterior se ha realizado correctamente. A continuación, se pretende ejecutar la aplicación minishell desarrollada por un grupo de alumnos en el laboratorio de Sistemas Operativos.
Respuesta:
- (f.1) ¿Cuáles son órdenes internas?
Solo
pwd. - (f.2) ¿Qué servicios (funciones de la API) POSIX de procesos se invocarán con la ejecución de cada orden?
pwd: Ninguna.make &:fork()yexec()../examenssoo | sort:fork(),exec(),wait()yexit().
Donde:
exec()representa a cualesquiera de los servicios POSIX de la familiaexec, aunque el servicio más adecuado esexecvp(). - (f.3) ¿Cuál va a ser el resultado de ejecutar la orden
./examenssoo | sort?Un error, porque no están implementadas las tuberías en la minishell.
- (f.4) ¿Qué procesos mostrará en pantalla la ejecución de la orden
ps?bash./minishell