Algunas Soluciones (Sincronización)

Anuncio
SOLUCIONES A ALGUNOS DE LOS EJERCICIOS
DE SINCRONIZACION Y COMUNICACION ENTRE PROCESOS
1. Con las tres llamadas create, suspend, y resume, la estructura del programa sería la siguiente:
process recoger;
......
suspend();
suspend();
recoger*;
resume(pid2);
resume(pid3);
.......
process imprimir;
........
suspend();
imprimir*;
resume(pid3);
..........
process guardar;
.......
suspend();
guardar*;
resume(pid1);
.......
process calcular;
.......
suspend();
suspend();
calcular*;
resume(pid1);
resume(pid4);
........
........
pid1 = create("recoger");
pid2 = create("guardar");
pid3 = create("calcular");
pid4 = create("imprimir");
resume(pid1); resume(pid1); resume(pid3);
Esta solución presupone que las llamadas a resume() siempre tienen un correspondiente suspend() que las
espera. En un sistema operativo si se hace una llamada para reanudar un proceso que no está suspendido el
efecto es normalmente nulo. Pero si esto tuviera lugar en este programa ocasionaría la pérdida de
señalizaciones con los correspondientes fallos de precedencia, y su funcionamiento sería incorrecto. Si
suponemos que resume() devuelve un valor TRUE o FALSE dependiendo de si ha reanudado o no
efectivamente el proceso que pretendía, podrían suplirse todas las llamadas a resume() por
while (! resume(pidi)) { } ;
pero aunque de este modo la lógica del problema estaría correctamente resuelta, sería a costa de pérdida de
eficacia de la solución al introducir bucles de reanudaciones que consumen tiempo de UCP sin hacer
trabajo útil.
S1.
a) Es segura, sin interbloqueo, y con posible aplazamiento indefinido..
Basta para ello advertir que inicialmente los valores de las variables c (global), d de p1 (local) y e de p2
(local) que intervienen en la negociación del acceso a la sección crítica forman la tripleta (1,0,0) y que las
únicas operaciones que se efectúan son intercambios indivisibles sin modificación de valores. Por tanto los
valores que se pueden alcanzar son:
(1,0,0) ==> p1 y p2 están fuera de sus Secciones Críticas
(0,1,0) ==> p1 está en su Sección Crítica, y p2 está fuera de la suya
(0,0,1) ==> p1 está fuera de su Sección Crítica y p2 dentro de la suya
Luego la exclusión mutua es segura.
Para producirse interbloqueo deberían alcanzarse los valores (0,0,0): ambos procesos fuera y sin posibilidad
de entrar, lo cual no es posible.
El aplazamiento indefinido es posible puesto que no hay ningún mecanismo que impida que los valores
estén oscilando entre (1,0,0) y (0,1,0), por ejemplo, sin dar opción a p2 a pasar a su sección crítica.
b) La generalización a n procesos es inmediata. Sólo hay que repetir el esquema de cualquiera de los
procesos p1 o p2 el número de veces que haga falta, para negociar la exclusión mutua con un (n+1)-tuple de
valores de los cuales uno vale 1 y el resto 0.
c) Si XCHG no fuera indivisible ya no se aseguraría la existencia invariante de uno y sólo un 1, y ello
podría ocasionar fallos de exclusión mutua (ej. (0,1,1)) e interbloqueos (ej.(0,0,0)).
1
S3.
a)
Definición original con cola de espera
WAIT:
if (s == 0))
suspender en s.Q;
else
s = s - 1;
Definición alternativa con cola de espera
WAIT:
s := s - 1;
if (s < 0))
suspender en s.Q;
SIGNAL:
if (! s.Q vacía)
reanudar un proceso de s.Q;
else
s = s + 1;
SIGNAL:
if (! s.Q vacía)
reanudar un proceso de s.Q;
{punto de interrupción}
s := s + 1;
La operación de decremento juntamente con la comprobación del valor de s debe formar un todo indivisible
en la operación wait. Para la operación signal basta que sean indivisibles separadamente la operación de
reanudación condicional y la de incremento.
b) El valor negativo indica Ocupado con operación de espera (wait) en curso y, en el caso de
implementación con cola de espera, el numero de procesos que han pasado a esperar desde la última
señalización
c) Sí afecta a SIGNAL. Hace que el incremento del valor del semáforo se haga siempre y no solo en el caso de que
no haya ningún proceso esperando en cola.
Otra posibilidad:
Siendo Pb(s) y Vb(s) las operaciones de espera y señalización sobre semáforos binarios, es decir,
Pb(s) ≡ while (s == 0) { } ; y Vb(s) ≡ s = 1; definimos
wait(s) : Pb(mutex)
s = s - 1;
if (s < 0) { Vb(mutex); Pb(retardo) };
Vb(mutex);
signal(s): Pb(mutex)
s = s + 1;
if (s <= 0) Vb(retardo) else Vb(mutex);
S5.
Declaraciones globales
sem_t sillon, afeitado; mutex_t mutex;
int sillas;
boolean esperando;
const int MAX = 5;
Procesos
Barbero()
Cliente()
2
while (TRUE) {
lock(mutex);
if (sillas == 0) {
esperando = TRUE;
unlock(mutex);
wait(sillon);
unlock(mutex);
esperando = FALSE
};
sillas = sillas-1;
unlock(mutex);
Afeitar;
post(afeitado);
};
lock(mutex);
if (sillas < MAX) {
sillas = sillas+1;
if (esperando)
post(sillon);
unlock(mutex);
wait(afeitado);
} else
unlock(mutex);
Inicialización (programa principal):
sillas = 0;
esperando = FALSE;
sem_init(sillon, 0);
sem_init(afeitado, 0);
mutex_init(mutex);
cobegin {
Barbero();
Clientes() //tantas instancias de Cliente como se precise
};
S5. Posible segunda solución. - ¿Funciona?
Barbero
while (TRUE) {
wait (sala);
lock (mutex);
sillas = sillas-1;
unlock (mutex);
post (afeitando);
Afeitar
};
Clientes
lock (mutex);
if (sillas < MAX) {
post (sala);
sillas = sillas+1;
unlock (mutex);
wait (afeitando)
} else
unlock (mutex)
Inicialización:
sillas = 0;
sem_init (afeitando, 1);
sem_init (sala, 0); // sala, semáforo general
mutex_init (mutex);
cobegin {
Barbero();
Clientes(), ...;
};
S7. (a)
El proceso LEER hace usos de dos procedimientos:
leetarjeta (car) que lee un tarjeta de 80 columnas y rellena el array car de 80 caracteres
put(buf, car)
que coloca el carácter car en la posición siguiente del buffer ilimitado buf
El proceso MODIFICAR hace uso de dos procedimientos
get(buf, car)
que toma el siguiente carácter del buffer ilimitado buf y lo asigna a car
3
Si el buffer esta vacío se queda esperando
put(buf, car)
El proceso IMPRIMIR hace uso de dos procedimientos
get(buf, car)
imprime(línea) que saca a la impresora el array línea de 125 caracteres
Si los buffers los definimos así:
typedef struct buffer {
int indent, indsal;
char elementos[infinito];
sem_t contador;
} BUFFER;
los procedimientos get() y put() serán:
get(BUFFER buf; char car) {
wait(buf.contador);
car = buf.elementos[buf.indsal]
buf.indsal = buf.indsal + 1;
};
put(BUFFER buf:; char car) {
buf.elementos[indent] = car;
buf.indent = buf.indent + 1;
post(buf.contador);
};
y los procesos quedarían básicamente de la siguiente forma, comunicados a través de dos buffer infinitos
buf1 y buf2:
Leer:
Modificar:
Imprimir:
i=0;
while (! eof) {
leetarjeta (car);
for (i=0; i<80; i++)
put(buf1,car[i]);
put(buf1,' ')
};
put(buf1,eof);
get(buf1, car1);
while (! eof) {
get(buf1,car2);
if ((car1=='*') &&
(car2=='*')) {
ristra = TRUE;
put(buf2,'/');
} else if (ristra)
ristra = false;
else
put(buf2, car1);
car1=car2;
};
if (! ristra)
put(buf2,car)
i=0;
while (! eof) {
get(buf2,car);
linea[i]=car;
i=i+1;
if (i == 125) {
imprime(linea);
i=0;
}
};
if (i != 0)
imprime(linea);
S8. (b)
// program PCconCE; {productor/consumidor con cuenta de eventos}
const int n = 5;
contadores producido, consumido;
elementos deposito[N];
void PRODUCTOR(int id) {
int ip;
ip = 0;
while (TRUE) {
await(consumido, ip-N);
deposito[ip % n] := producir();
ip = ip+1;
advance(producido);
}
4
};
void CONSUMIDOR(int id) {
int ic;
ic = 0;
while (TRUE) {
await(producido, ic);
consumir(deposito[ic % n]);
ic = ic+1;
advance(consumido)
}
};
main() {
initial_event(producido,0);
initial_event(consumido,0);
cobegin {
PRODUCTOR(0);
CONSUMIDOR(0);
};
}.
MVC1.
Una posible solución a este problema, con semáforos, es la siguiente:
// Program Fumadores;
sem_t ingrediente[3];
sem_t s[6];
mutex mutex ; sem_t sem;
int t;
void Agente() {
int i, j;
while (TRUE) {
i = random(2) + 1;
j = i;
while (j == i)
j = random(2) + 1;
wait(sem);
post(ingrediente[i]);
post(ingrediente[j]);
end;
end;
void Ayuda(int id) {
const int incr[3] = {1,2,4};
while (TRUE) {
wait(ingrediente[id]);
lock(mutex);
t = t + incr[id];
post(s[t]);
unlock(mutex);
};
};
};
void Fumador(int id) {
const int nec[3] = (6,5,3);
while (TRUE) {
wait(s[nec[id]]);
t = 0;
post(sem);
--- FUMA --};
5
main() {
t = 0;
sem_init(sem, 1);
mutex_init(mutex, 1);
cobegin {
Agente();
Ayuda(0);Ayuda(1); Ayuda(2);
Fumador(0);Fumador(1);Fumador(2);
}
}.
mutex se encarga de forzar la exclusión mutua al operar con t
sem (Agente espera y Fumador[ ] señala) : permite que Agente saque un nuevo par de ingredientes
t acumula los identificadores de hasta 2 ingredientes
Resuelto con mutexes y variables condicionales
// Program Fumadores;
struct ingredientes {boolean tabaco,cerillas,papel;} = {FALSE,FALSE,FALSE};
mutex_t ingr_mutex;
cond_t ingr_cond;
void Agente() {
int i, j;
while (TRUE) {
i = random(2) + 1;
j = i;
while (j == i) j = random(2) + 1;
lock (ingr_mutex)
while (tabaco || cerillas || papel)
wait(ingr_cond2, ingr_mutex)
tabaco = (i == 0) || (j == 0);
cerillas = (i == 1) || (j == 1);
papel = (i == 2) || (j == 2);
broadcast(ingr_cond1);
unlock (ingr_mutex)
};
};
void FumadorTC() {
while (TRUE) {
lock(ingr_mutex);
while (!(tabaco && cerillas))
wait(ingr_cond1, ingr_mutex);
tabaco = FALSE;
cerillas = FALSE;
signal(ingr_cond2);
unlock(ingr_mutex);
--- FUMA --};
};
main() {
6
void FumadorPC() {
semejante a Fumador TC
pero con papel y cerillas
}
void FumadorTP {
semejante a Fumador TC
pero con tabaco y papel
}
cobegin {
Agente();
FumadorTC(); FumadorPC(); FumadorTP();
}
}
MVC4.
// programa ascensor
Fichero CONTROL.c
const int Npisos = 10;
cond_t Petpisos[Npisos], aviso;
int NpetPiso[Npisos];
mutex_t ctrlme;
int piso_actual=1, piso_nuevo=-1, peticiones=0;
void PulsarBoton(int piso) {
lock(ctrlme);
signalc(aviso);
peticiones = peticiones+1;
NpetPiso[piso]++;
waitc(Petpisos[piso],ctrlme);
NpetPiso[piso]--;
unlock(ctrlme);
};
void EsperarPeticiones() {
lock(ctrlme);
if (peticiones == 0) waitc(aviso,ctrlme);
unlock(ctrlme);
};
void ElegirMasCercano(int& dist) {
int i;
boolean sigue;
lock(ctrlme);
i= 0; sigue = TRUE;
while ((i <= Npisos) && sigue) {
if ((piso_actual+i <= Npisos) && (NpetPiso[piso_actual+i]>0)) {
piso_nuevo = piso_actual+i;
dist = i; sigue = FALSE;
} else if ((piso_actual-i > 0) && (NpetPiso[piso_actual+i]>0)) {
piso_nuevo = piso_actual -i;
dist = -i; sigue = FALSE;
};
i = i + 1;
};
unlock(ctrlme);
};
void SubirBajar() {
lock(ctrlme);
while (NpetPiso[piso_nuevo])>0) {
signal(Petpisos[piso_nuevo]);
peticiones = peticiones-1;
};
7
piso_actual = piso_nuevo
unlock(ctrlme);
};
void pasajero() {
int origen, destino;
origen = random(Npisos)+1;
while (TRUE) {
PulsarBoton(origen);
do {
destino = random(Npisos)+1
} while (destino == origen);
PulsarBoton(destino);
origen = destino;
sleep(random(10));
}
}
void servidor() {
integer dist,
while (TRUE) {
EsperarPeticiones;
ElegirMasCercano(dist);
sleep(ABS(dist));
SubirBajar;
}
}
main() {
cobegin {
servidor;
pasajero();pasajero();pasajero(); ...;
}
}
MVC5.
La definición de las operaciones P (wait) y V (signal) para semáforos acotados son las siguientes:
P(s):
while (s <= 0) { };
s := s - 1;
V(s):
while (s >= smax) { };
s := s + 1;
El monitor que fuerza este comportamiento es el siguiente:
Fichero (monitor) Sa
const int smax = N, sem[NS];
cond_t c, d;
mutex_t Saem
void P(int s) {
lock(Saem);
while (sem[s] <= 0) wait(c,Saem);
8
sem[s]--;
signal(d); // if (sem[s] < smax) signal(c);
unlock(Saem);
};
void V(int s);
lock(Saem);
if (sem[s] >= smax) wait(d, Saem);
sem[s]++;
signal(c); // if (sem[s] > 0) signal(c);
unlock(Saem);
};
MVC6.
Fichero (monitor) LyE {
int nlect=0, Lesp=0, Eesp=0;
bool ocupado=FALSE;
cond_t condL, condE;
mutex_t LyEem;
void IniciarL () {
void AcabarL() {
lock(LyEem);
lock(LyEem);
if (ocupado||(Eesp > 0)) {
nlect = nlect-1;
if (nlect == 0)
Lesp = Lesp+1;
wait(condL,LyEem);
signal(condE);
unlock(LyEem);
Lesp = Lesp-1;
};
};
nlect = nlect+1;
signalc(condL);
unlock(LyEem);
};
void IniciarE() {
lock(LyEem);
if (ocupado||(nlect!=0) {
Eesp = Eesp+1;
wait(condE,LyEem);
Eesp = Eesp-1;
};
ocupado = TRUE;
unlock(LyEem);
};
void AcabarE() {
lock(LyEem);
ocupado = FALSE;
if (Lesp > 0)
signal(condL);
else
signal(condE);
unlock(LyEem);
};
Otra solución, si se dispone de la función empty(cola) que devuelve un valor lógico true o false:
Fichero (monitor) LyE {
int nlect=0;
boolean ocupado=FALSE;
cond_t condL, condE;
mutext_t LyEem;
9
void IniciarL() {
lock(LyEem);
if (ocupado || !empty(condE))
wait(condL, LyEem);
nlect = nlect+1;
signal(condL);
unlock(LyEem);
};
void AcabarL() {
lock(LyEem);
nlect = nlect-1;
if (nlect == 0)
signal(condE);
unlock(LyEem);
};
void IniciarE;
lock(LyEem);
if (ocupado || (nlect != 0))
wait(condE, LyEem);
ocupado = TRUE;
unlock(LyEem);
};
void AcabarE() {
lock(LyEem);
ocupado = FALSE;
if (!empty(condL))
signal(condL);
else
signal(condE);
unlock(LyEem);
};
}
MVC7.
// programa Filosofos;
Fichero(monitor) Mpalillos
int Palillos[5]={2,2,2,2,2};
cond_t OK_Comer[5];
mutex_t Mpalem;
int j;
// rango 0..2;
void Coger_Palillos (int i){
lock(Mpalem);
while (Palillos[i] != 2) wait(OK_Comer[i],Mpalem);
Palillos[(i+1) % 5] = Palillos[(i+1) % 5] - 1;
Palillos[(i-1) % 5] = Palillos[(i-1) % 5] – 1
unlock(Mpalem);
}
void Soltar_Palillos (int
lock(Mpalem);
Palillos[(I+1) % 5] =
Palillos[(I-1) % 5] =
signal(OK_Comer[(I+1)
signal(OK_Comer[(I-1)
unlock(Mpalem);
}
} // MPalillos.
i) {
Palillos[(I+1) % 5] + 1;
Palillos[(I-1) % 5] + 1;
% 5]);
% 5]);
void Filosofo (int i) {
while (TRUE) {
Pensar;
Coger_Palillos(i);
Comer;
Soltar_Palillos(i);
}
}
10
main() {
cobegin { Filosofo(0); Filosofo(1);
Filosofo(2); Filosofo(3); Filosofo(4); }
}
Demostración de la no existencia de interbloqueo:
Si hubiera interbloqueo,
nadie estaría comiendo
Î SUMA (Palillos[]) = 10
todos están en espera
Î SUMA (Palillos[]) <= 5
lo cual, evidentemente, es una contradicción.
Puede existir aplazamiento indefinido al no haber una política que controle la actuación de los filósofos,
por lo cual dos de ellos inmediatamente no consecutivos pueden cooperar para impedir la entrada a comer del
filósofo intermedio.
ME1.
Basta sustituir
wait por receive(mutex,msg) y signal por send(mutex, null)
Lector
while (TRUE) {
receive(mutex, msg);
nlect = nlect + 1;
if (nlect == 1)
receive(escr, msg);
send(mutex, null);
LEER
receive(mutex, msg);
nlect = nlect - 1;
if (nlect == 0)
send(escr, null);
send(mutex, null);
Otro proceso;
};
Escritor:
while (TRUE) {
receive(escr, msg);
ESCRIBIR;
send(escr, null);
Otro_proceso
}
inicialización
nlect = 0;
send(mutex, null);
send(escr, null);
cobegin {
lectores; escritores;
};
ME2.
a) Mira alternativamente en cada buzón cada cierto
tiempo
b) Emplea un proceso separado para atender a cada
buzón y se comunica con ellos a través de un buzón
común
P1:
while (TRUE) {
receive(M1,msg);send(MIyM2, msg);
}
P2:
while (TRUE) {
receive(M2,msg);send(MIyM2, msg);
}
boolean mirar(buzon, &msg) {
receive(buzon, m);
if (m!=null) {msg=m;return TRUE}
else {send(buzon, null);
return FALSE;}
}
while (TRUE) {
while (mirar(M1,msg))
procesar(msg);
while (mirar(M2,msg))
procesar(msg);
sleep(rato);
}
while (TRUE) {
receive(M1yM2, msg);
procesar(msg);
}
11
Nota: el mensaje “null” enviado en el apartado a) debe tener la menor prioridad posible, para
garantizar que siempre es el último del buzón.
12
Descargar