PRÁCTICAS DE LABORATORIO DE SISTEMAS OPERATIVOS
PRIMERA PRÁCTICA EVALUABLE (2008-09)
Tú la llevas
Enunciado.
El programa que hay que presentar constará de un único
fichero fuente de nombre lallevas.c
. La
correcta compilación de dicho programa, producirá un
fichero ejecutable, cuyo nombre será obligatoriamente
lallevas
. Respetad las mayúsculas/minúsculas
de los nombres, si las hubiere.
La ejecución del programa creará una serie de procesos
que accederán a una zona de exclusión mutua.
La regulación del acceso a dicha zona se hará
mediante el paso de un testigo.
Los procesos se dispondrán en círculo. Mientras
ningún proceso quiera entrar en la sección
crítica, el testigo circula por el círculo
de procesos. Cuando un proceso quiera entrar, ha de esperar
a estar en posesión del testigo. Entrará
entonces en la sección crítica. Cuando salga,
procederá con la circulación del testigo.
La invocación de la práctica se hará
con dos argumentos, el último de ellos opcional:
lallevas n_procs [debug]
El primero es un número entero comprendido entre
3 y 33. Es el número de procesos que participan
en la práctica. De ellos, uno es el padre y el
resto son hijos suyos. El modo en que se forma el corro
para la circulación del testigo se deja a la
libertad de los autores.
Si el segundo argumento es la palabra debug, la
práctica se ejecuta en modo de depuración.
Las características de este modo se explican más
abajo. Si los parámetros introducidos no respetan
las reglas anteriores, el programa lo detectará,
informará al usuario y acabará.
Funcionamiento de cada proceso
Durante el período de funcionamiento de la práctica,
los procesos estarán en el siguiente bucle infinito:
- Si estamos en el modo de depuración, duerme 1
segundo. Si no, no hace nada.
- Entra en la sección crítica.
- Si estamos en el modo de depuración, imprime
en la pantalla: E(pid). Si no, imprime
solamente una E. En ningún caso imprimirá
un salto de línea.
- Si estamos en el modo de depuración, duerme 2
segundo. Si no, no hace nada.
- Si estamos en el modo de depuración, imprime
en la pantalla: S(pid). Si no, imprime
solamente una S. En ningún caso imprimirá
un salto de línea.
- Sale de la sección crítica.
Viendo la salida por pantalla, es evidente que para que la
práctica funcione es necesario que aparezcan en alternancia
perfecta, Ees y eSes.
El paso del testigo se va a simular mediante el envío de
señales. Cuando un proceso en posesión del
testigo, desea pasárselo a otro, le enviará la
señal SIGUSR1
. Un proceso que no tenga
el testigo pasa a poseerlo cuando reciba la señal.
Si debe reenviarlo, esperará al menos una dé
cima de segundo si estamos en el modo de depuración.
De no estar en dicho modo, el reenvío será
inmediato. Para efectuar esa pausa, se usará la
función nanosleep
(mirad la página de
manual).
Para hacer que el proceso duerma en el resto de casos, se
usará la señal SIGALRM
, nunca la
función sleep
. Dormir hará que el proceso,
en modo de depuración, apenas consuma CPU. Lo podéis
comprobar con la orden top
.
Para que el buffer intermedio usado por printf
no interfiera con la salida de los procesos, es importante
usar write
para la salida por pantalla en su lugar.
Finalización ordenada
La práctica acabará cuando el usuario pulse
CTRL-C. Los procesos deben morir y el padre, una vez hayan
muerto todos imprimirá un salto de línea y
la frase: "Programa acabado correctamente".
Restricciones
- Se deberán usar llamadas al sistema siempre que sea
posible, a no ser que se especifique lo contrario.
- No está permitido usar la función de biblioteca
system
, salvo indicación explícita en
el enunciado de la práctica.
- No se puede suponer que los PIDs de los procesos
de una ristra van a aparecer consecutivos. Puestos
en plan exquisito, ni siquiera podemos suponer que
estarán ordenados de menor a mayor (puede ocurrir
que se agoten los PIDs y se retome la cuenta partiendo
de cero).
- No está permitido el uso de ficheros, tuberías u
otro mecanismo externo para transmitir información
entre los procesos, salvo que se indique en el enunciado.
- Supondremos un límite máximo de procesos
igual a 33. Este límite os puede servir
para no tener que usar memoria dinámica si no lo
deseáis.
Plazo de presentación.
Hasta el lunes, 23 de marzo 2009, inclusive.
Normas de presentación.
Acá están.
LPEs.
- Las tareas que tiene que realizar el
programa son variadas. Os recomiendo que vayáis
programándolas y comprobándolas una a una. No es muy
productivo hacer todo el programa de seguido y corregir
los errores al final. El esquema que os recomiendo
seguir para facilitaros la labor se os muestra a
continuación:
- Haced un pequeño programa que cree un
hijo y deje a padre e hijo en un
pause()
. Compiladlo, ejecutadlo,
comprobadlo con ps -fu
y
depuradlo, si fuera necesario.
- El siguiente paso es modificar el programa
para que tenga 7 hijos.
- Si todo va bien, lograd que se tengan tantos
hijos como los especificados en una variable.
- Es el momento de que se creen tantos hijos
como se indique en la línea de órdenes.
- Completad el tratamiento de la línea de
órdenes, añadiendo la posibilidad
de
debug
y detectando errores.
- Ahora empecemos a programar la finalización
ordenada. Al pulsar CTRL+C, el padre debe esperar
por la muerte de los hijos e imprimir por pantalla
su mensaje. Registrará para ello la
señal
SIGINT
.
- El siguiente paso consiste en hacer circular el
testigo. Debéis registrar la señal
SIGUSR1
y diseñar un esquema
de circulación de la señal.
Haced la pausa del modo de depuración.
Los procesos pueden imprimir su pid cada vez que
reciban el testigo para poder depurar. Uno
de los procesos debe comenzar la circulación
del testigo.
- Si no funciona, pensad dónde se está
recibiendo la señal
y si la solución que planteamos es válida
se reciba donde se reciba. Pensad que también
puede estar recibiéndose la siguiente señal
en la propia manejadora. Si no funciona,
una solución es bloquear con
sigprocmask
SIGUSR1 en la zona
conflictiva y desbloquearla cuando no tenga
peligro el recibirla (quizá en un
sigsuspend
).
- El resto del camino no es más fácil.
Debéis programar la entrada y salida de
la sección crítica y las pausas
correspondientes, registrando la señal
SIGALRM
. El problema fundamental de
esta etapa es que tenéis que poner orden
en la posible recepción concurrente de varias
señales.
- Si veis que la cosa se complica, quizá
podéis replantear la programación
usada a una orientada a sucesos, tal y como vimos
en clase...
- No se puede usar
sleep()
o
similares para sincronizar los procesos. Hay que
usar otros mecanismos.
- Sabéis que si usáis espera ocupada en lugares
donde explícitamente no se haya dicho que se puede
usar, la práctica está suspensa. No obstante, existe
una variedad de espera ocupada que podríamos
denominar espera semiocupada. Consiste en
introducir una espera de algún segundo en cada
iteración
del bucle de espera ocupada. Con esto el proceso no
consume tanta CPU como en la espera ocupada, pero persisten
los demás problemas de la técnica del sondeo,
en particular
el relativo a la elección del periodo de espera.
Aunque la práctica no estará suspensa si hacéis
espera
semiocupada, se penalizará en la nota bastante si la
usáis. En conveniente que la evitéis.
- Evitad, en lo posible, el uso de variables globales.
Tenéis la posibilidad de declarar variables
estáticas.
- Tened cuidado con el uso de
pause()
.
Salvo en bucles infinitos de pause
s,
su uso puede estar mal. Mirad la solución a la
práctica
propuesta en la sesión quinta acerca de él o el
siguiente LPE.
- El programa que he hecho se para a veces.
O en mi casa se para, pero en clase, no.
O en clase sí, pero en casa, no.
Solución.
- ¿Qué hago cuando mi programa se desboca
para no perjudicar el funcionamiento de la
máquina?
Solución.
Prácticas propuestas en años anteriores.
© 2009 Guillermo González Talaván.