En este problema se propone buscar una solución que gestione las operaciones, tanto de lectura como de escritura, que realizan unos usuarios sobre la información almacenada en varios discos independientes. Como se proponía en el gráfico del enunciado, cada disco va a tener asociados: un proceso Manejador (manipulador directo) del disco y un proceso Servidor que atiende y gestiona todas las peticiones que se hagan sobre él. Todos los discos son similares, y se diferencian porque cada uno tiene un identificador diferente. Por tanto, la solución del problema sólo requiere la implementación de tres tipos de tareas: Manejador, Servidor y Cliente.
Obsérvese la función que desempeñan las operaciones de las colas de peticiones, en la implementación de la sincronización en el proceso servidor. Por otro lado, resulta relevante el hecho de que el proceso manejador desempeñe una actitud activa en la ejecución, no teniendo que esperar a que exista una petición de operación del Servidor, sino adelantándose a ésta e indicando su situación de ``preparado'' para operar sobre el disco.
MODULE Discos;
FROM COLAS IMPORT TipoCola, (* TipoPeticion *)
CreaVacia,
Llena,
Vacia,
Insertar,
Borrar,
Primero;
CONST NumDiscos = 3;
NumClientes = 5;
TYPE TipoDato = INTEGER;
TYPE TipoPeticion = RECORD
sector: INTEGER;
CASE esLectura: BOOLEAN OF
TRUE: cliente: INTEGER |
FALSE: dato: TipoDato
END
END;
TYPE RespuestaLectura = RECORD
dato: TipoDato;
cliente: INTEGER
END;
VAR datoLeido: ARRAY [1..NumClientes] OF CHANNEL; (* cliente *)
VAR pedirOperacion,
pedirOrden,
retornoLectura, (* servidor *)
orden: (* disco *)
ARRAY [1..NumDiscos] OF CHANNEL;
TASK Manejador (i: INTEGER);
VAR peticion: TipoPeticion;
respuesta: RespuestaLectura;
BEGIN
(*... iniciar disco ...*)
LOOP
Send(pedirOrden[i], TRUE);
Receive(orden[i], peticion);
IF peticion.esLectura THEN
(*... Leer 'respuesta.dato' del sector 'peticion.sector' ...*)
respuesta.cliente := peticion.cliente;
Send(retornoLectura[i], respuesta)
ELSE
(*... Escribir 'peticion.dato' en 'peticion.sector' ...*)
END
END
END Manejador;
TASK Servidor(i: INTEGER);
VAR cola: TipoCola;
x: BOOLEAN;
peticion: TipoPeticion;
respuesta: RespuestaLectura;
BEGIN
GetChannel(pedirOperacion[i]);
GetChannel(pedirOrden[i]);
GetChannel(retornoLectura);
CrearVacia (cola);
LOOP
SELECT
WHEN NOT Llena(cola), Receive(pedirOperacion[i], peticion) DO
Insertar(cola, peticion)
WHEN NOT Vacia(cola), Receive(pedirOrden[i], x) DO
Primero (cola, peticion);
Borrar (cola);
Send(orden[i], peticion)
WHEN TRUE, Receive(retornoLectura[i], respuesta) DO
Send(datoLeido[respuesta.cliente], respuesta.dato)
END
END
END Servidor;
La actividad de los clientes está sujeta a la ejecución de una de las dos operaciones (Leer o Escribir) para actuar sobre el disco. Dichas operaciones son, precisamente, los ``servicios'' que ofrece cada servidor de disco a los clientes. Junto a todo esto, la implementación de las operaciones debe hacerse bajo la forma de procedimientos con unas cabeceras determinadas.
Las operaciones de lectura y escritura son diferentes en cuanto que lo es el sentido de la comunicación que producen. Así, mientras que la operación de escritura no implica la espera del proceso cliente (una vez se haya notificado al servidor la petición no hay que esperar a que se escriba realmente), para el caso de la lectura esto no es así, y el proceso cliente debe esperar a recibir los datos que haya solicitado. Lo anterior tiene una trascendencia mayor que la esperada para el caso de las lecturas:
... para que los datos puedan ser remitidos desde el servidor al cliente, éste previamente debe haberle indicado cuál es el canal por el que deberá enviarle dichos datos.
Como el enunciado obligaba a la utilización de unas cabeceras de procedimientos en las cuales no se hace ninguna mención al canal de recepción, la solución pasa por incluir, cuando la operación es de lectura, el canal de recepción dentro del mensaje enviado al servidor.
TASK Cliente(i: INTEGER);
VAR
disco,
sector : CARDINAL;
dato : TipoDato;
PROCEDURE Leer(disco, sector: INTEGER; VAR dato: TipoDato);
VAR lectura: PeticionLectura;
BEGIN
lectura.sector := sector;
lectura.cliente := i;
Send(pedirLectura[disco], lectura);
Receive(datoLeido[i], dato)
END Leer;
PROCEDURE Escribir(disco, sector: INTEGER; dato: TipoDato);
VAR escritura: PeticionEscritura;
BEGIN
escritura.sector := sector;
escritura.dato := dato;
Send(pedirEscritura[disco], escritura)
END Escribir;
BEGIN
GetChannel(datoLeido[i]);
LOOP
(* .. Operaciones de lectura y/o escritura .. *)
END
END Cliente;
VAR k: INTEGER;
BEGIN
COBEGIN
FORALL k := 1 TO NumDiscos DO
Manejador(k);
Servidor(k)
END;
FORALL k := 1 TO NumClientes DO
Cliente(k)
END;
COEND
END Discos.