Home UC3M
Home IT

Laboratorio 4

Simulación del ciclo de vida de los procesos

 OBJETIVOS

En esta práctica vamos a simular el ciclo de vida de procesos ejecutados y controlados por un sistema operativo. Los procesos son creados, ocupan y liberan la CPU repetidas veces hasta que finalmente terminan. El Sistema Operativo debe mantener una estructura de datos con todos los procesos existentes en el sistema en cada momento. Cada elemento de la estructura de datos almacena la información necesaria de uno de los procesos del sistema: su identificador de proceso (PID) y el estado del proceso tras el último cambio de contexto en el que el proceso abandonó la CPU. Para simplificar, no se contempla que los procesos se bloqueen en operaciones de entrada/salida.


 COMPONENTES DEL MODELO

El código C necesario para ejecutar la simulación se divide en 6 ficheros, de los cuales se proporcionan 5:

  • Ficheros CPU.c y CPU.h. Representa la CPU de nuestro ordenador. Nuestra CPU contiene 3 datos que deben ser guardadas y restauradas en cada cambio de contexto:

    • El contador de programa. Su valor puede ser obtenido por la función int getCPUpc(), y puede ser modificado por la función void setCPUpc(int).

    • El puntero de pila. Su valor puede ser obtenido por la función int getCPUsp(), y puede ser modificado por la función void setCPUsp(int).

    • 16 registros de propósito general. Sus valores pueden ser obtenidos por la función int* getCPUreg(), y pueden ser modificados por la función void setCPUreg(int*). Tanto el resultado de getCPUreg como el argumento de setCPUreg son un vector de 16 enteros almacenados en memoria creada dinámicamente. Nótese que getCPUreg reserva una nueva porción de memoria cada vez que es llamada y que tanto getCPUreg como setCPUreg no liberan memoria.

    Existe además otra función en el fichero CPU.c. Su prototipo es void step(), y simula el comportamiento de un proceso durante un período de ejecución en la CPU. Para ello, modifica aleatoriamente el contenido del contador de programa, el puntero de pila y los registros de propósito general.

  • Fichero process.c Este fichero contiene las variables y funciones necesarias para llevar cuenta de los procesos actualmente disponibles en el sistema. Debe también llevar cuenta de cuál es el proceso que está en cierto momento ocupando la CPU. Es la única parte que se debe implementar en la práctica, ya que los demás ficheros se proporcionan. El fichero process.c deberá implementar las siguientes funciones:

    • void Init(). Inicializa la estructura de datos: cola de PCBs y le añade un proceso cuyo PID deberá ser igual a 0. Puede ser ejecutado, pero no terminará hasta el final de la simulación. El valor inicial del contador de programa, puntero de pila y los registros de propósito general para este proceso ha de ser 0. Después de una llamada a esta función (al principio de la simulación) se supone que ningún proceso está ejecutando. Por este motivo, en la función main, explicada más adelante, la llamada a Init, va seguida inmediatamente de una llamada a ContextSwitch , para hacer que el proceso creado ocupe la CPU.

    • int Clone(). Crea un nuevo proceso. El nuevo proceso tendrá inicialmente los mismos valores para el contador de programa, puntero de pila y registros de propósito general que el proceso que está ejecutándose en la CPU en ese momento. Clone asigna un PID al nuevo proceso y lo añade a la lista de procesos en el sistema. Se debe tener en cuenta que no puede ocurrir que 2 procesos tengan un mismo PID y que el rango de valores de PID permitido es entre 0 y 100. Teniendo en cuenta que el PID 0 está reservado al proceso creado inicialmente, el rango de valores de PIDs de procesos creados con Clone debe estar entre 1 y 100. No es necesario considerar el caso en el que se produce una llamada a Clone y no hay ningún PID disponible, ya que esa situación no se produce durante la simulación. Clone devuelve el PID del proceso creado. Como resultado de una llamada a Clone no cambia el proceso que está ocupando la CPU.

    • void Terminate(). Termina el proceso que está en este momento ocupando la CPU, liberando todos los recursos dedicados a dicho proceso en la estructura de datos. Como resultado de una llamada a Terminate, la CPU quedará libre. Debido a esto, una llamada a Terminate vendrá seguida siempre por una llamada a ContextSwitch para hacer que otro proceso pase a ocupar la CPU, excepto después de que todos los procesos del sistema hayan terminado, al final de la simulación.

    • void ContextSwitch(int pid). Si había algún proceso ejecutando, guarda su contexto (contador de programa, puntero de pila y registros de propósito general). En cualquier caso, hace que el proceso cuyo PID es pid pase a ocupar la CPU. Para ello, el contexto del nuevo proceso debe ser restaurado en la CPU.

    Es obligatorio almacenar la estructura de datos con los procesos del sistema utilizando memoria dinámica.

  • Ficheros process.h. y procVal.h Contienen la definición de los prototipos (tipo del resultado, nombre y tipo de los parámetros) de las funciones descritas en el apartado anterior. Deben ser incluido al comienzo del fichero process.c.

  • Fichero main.c. Ejecuta la simulación. Comienza con una llamada a Init, seguida de una llamada a ContextSwitch, para hacer que el proceso con PID 0 ejecute en la CPU. A continuación se realizan una serie de llamadas a Clone(), Terminate() y ContextSwitch(). Al final de la simulación, termina todos los procesos que estuviesen en el sistema e imprime un mensaje indicando que la simulación se ha realizado con éxito.

  • Fichero procVal.a. Implementa las funciones void CloneVal(int), void TerminateVal(), void InitVal(), void CPUCheck(), y int getRandomPID(). Estas funciones se usan para comprobar que el código realizado por el alumno es correcto. La única cosa que necesitamos conocer sobre ellas es que getRandomPID selecciona aleatoriamente uno de los procesos disponibles en el sistema y devuelve su PID.

La figura 1 muestra la interacción de los cuatro ficheros anteriores. En el fichero main.c se realizan una serie de pruebas del código desarrollado en el fichero process.c que a su vez ha de utilizar las funciones contenidas en CPU.c. En la función main se comprueban los resultados obtenidos con los producidos por el código contenido en procVal.a.

Figura 1. Interacción entre los ficheros de la aplicación

Interacción entre los ficheros de la aplicación

Compilación y prueba

Para compilar la práctica, se invocará el siguiente comando:

gcc -I ../LibAO -Wall -o process main.c CPU.c process.c procVal.a ../LibAO/libao.a

Al ejecutar el programa, si no hay errores, se produce exclusivamente el mensaje:

Execution with no errors

En caso de que se produzca un error, aparecerá un mensaje explicativo del error producido empezando por la palabra ERROR. En cualquier caso aparecerán los mensajes habituales relativos a gestión de memoria dinámica.

La aplicación debe ser desarrollada en Linux.

 

 ENVÍO

Solo debe subirse un fichero, process.c Deadline: 02/11/2012 17:00
Los nombres de los autores y sus NIAS deben indicarse en el comentario que aparece en process.c de la siguiente forma:
/* AUTHORS: NIA1;Name1;NIA2;Name2 */
Las prácticas no deben ser entregadas por las dos personas de una misma pareja de prácticas. Basta con que la entregue uno de ellos en Aula Global 2