Sistemas Operativos
Convocatoria de junio, año 2000.

Soluciones del examen
 

1. TEST

(1,a) (2,b) (3,b) (4,c) (5,a) (6,d) (7,d) (8,d) (9,a) (10,c) (11,c) (12,a) (13,a) (14,b) (15,d)

2. Cuestiones sobre Nachos

  1. La operación Yield() cede la CPU a un hilo de la cola de preparados del planificador, mientras que el hilo que la invoca se inserta al final de dicha cola. En el caso de que la cola de preparados esté vacía, entonces no se produce cesión alguna de CPU y el hilo que la invoca se sigue ejecutando.
  2. La política de memoria empleada inicialmente es la contigua de una sola partición, soportada sobre un hardware de paginación.
  3. Mediante una llamada al sistema, una interrupción o una excepción. Todas ellas son interceptadas por el emulador MIPS del Nachos, el cual invoca a la rutina manejadora de excepciones ExceptionHandler( ) ya en modo núcleo.
        El paso de modo núcleo a modo usuario se hace al invocar a machine->Run(), que entrega el control al emulador MIPS; o bien cuando la función ExceptionHandler() retorna.
    1. Los programas de usuario se compilan por separado del Nachos porque éstos no forman parte del sistema operativo y son programas autónomos, con su propia función main(), que se cargan dinámicamente a voluntad del usuario. Las opciones de compilación, las bibliotecas de funciones y los pasos necesarios para dejarlos listos para ejecutar son distintos.
         Se utilizan compiladores diferentes, en nuestro caso, debido a que se ejecutan en máquinas diferentes: el NACHOS corre sobre una máquina LINUX que utiliza un microprocesador Intel (se utiliza el compilador nativo de la máquina LINUX) y los programas de usuario del NACHOS que utilizan una máquina MIPS emulada, para lo cual se requiere un compilador que genere código MIPS.

3. Cuestión sobre memoria:

Si realizamos la traza de ambas políticas, observamos que la política LRU incurre en 7 fallos de página, mientras que la óptima sólo da 6 fallos. Una política da mejores resultados cuantos menos fallos de página produzca, luego en este caso la óptima da un mejor resultado.
La decisión equivocada de la LRU ha sido escoger como víctima la página 4 en uno de los últimos accesos.

 
 

ÓPTIMA:

(4,-,-) (4,2,-) (4,2,1) (4,2,1) (3,2,1) (3,2,1) (3,2,1) (4,2,1) (4,2,1) (4,2,1) (4,2,3) (4,2,3) (4,2,3) (4,2,3) (4,2,3)
 
 

LRU:

(4,-,-) (2,4,-) (1,2,4) (2,1,4) (3,21) (1,3,2) (2,1,3) (4,2,1) (1,4,2) (2,1,4) (3,2,1) (2,3,1) (4,2,3) (2,4,3) (3,2,4)
 
 

4. Cuestión sobre ficheros:

En todos los supuestos, el archivo entero se pierde si el daño se produce en el bloque donde está la información de control del archivo (bloque de comienzo, longitud, etc.), así que hemos de encontrar casos distintivos de cada política.
En el caso de la contigua, si se pierde un bloque de datos, el resto de ellos sigue siendo accesible, puesto que la información de ubicación no resulta dañada. En la política encadenada, también se pierde el acceso a la totalidad del archivo si se daña el primer bloque de datos, dado que perdemos el resto de la cadena. (Esto no ocurriría si se utilizaran encadenamientos dobles, o en sistemas basados en FAT). Por su parte, la política indexada tiene el riesgo de que se dañe el bloque de índices, en cuyo caso no podríamos acceder a ningún bloque del archivo. Por tanto, la probabilidad es mayor para las políticas encadenada e indexada respecto a la contigua. Entre la encadenada y la indexada vemos que la probabilidad es más o menos similar (aprox. 1 bloque sobre N posibles). Eso sí, si consideramos encadenamientos dobles, la indexada tendría más probabilidad.

 
 

5. Implementación del planificación usando cerrojos y variables de tipo condición

En este propuesta se utiliza el estilo Hoare para las varibles de tipo condición. Recuérdese que en este estilo cuando un proceso realiza una operación "signal" sobre una variable de tipo condición cede el cerrojo al proceso que había hecho una operación "wait" y se bloquea. El proceso que había realizado la operación "wait" entra en ejecución inmediatamente. El proceso que hizo la operación "signal" tiene que esperar a tomar de nuevo el control del cerrojo para seguir ejecutándose.
Un ejercicio que se plantea al lector es comprobar si la solución aportada serviría si se utilizaran varibales de tipo condición según el estilo de Mesa y de no ser válida aportar una solución.

Clase Planificador

	Sección de datos públicos globales:
		Proceso: variable entera que identifica el proceso que tiene el recurso asignado
		Ocupado: variable booleana que indica si el recurso está asignado o no.
		Llave: variable de tipo cerrojo
		Turno: variable de tipo condición, vinculada al cerrojo "Llave"
		Nespera: variable entera que indica cuántos procesos esperan por el recurso
		

	Valores iniciales:
		Ocupado = falso;
		Proceso = - 1;
		Nespera = 0;
		
	Sección de funciones públicas:
		Función Pido_Recurso( variable entera pid )
		{
			Retorno: variable local entera;
			Llave.adquiero;
			Si ((Ocupado=cierto)AND(Proceso=pid)) Entonces
				Retorno = -1;
			Si no
				Si (Ocupado=cierto) Entonces
					Nespera = Nespera + 1;
					Turno.wait;
					Nespera = Nespera – 1;
				Fin si
				Ocupado = cierto;
				Proceso = pid;
				Retorno = 0;
			Fin Si
			Llave.libero;
			Return(Retorno);
		}
					
	

		Función Libero_Recurso( variable entera pid )
		{
			Retorno: variable local entera;
			Llave.adquiero;
			Si ( (Ocupado = falso) OR (Proceso /= pid) ) Entonces
				Retorno := -1;
			Si no
				Ocupado = falso;
				Si ( Nespera > 0 ) Entonces
					Turno.señal;
				Fin si
				Retorno = 0;
			Fin Si
			Llave.libero;
			Return (Retorno);
		}

Fin de la clase