Contenido Coordinación entre Procesos Parte I • Semáforo: mecanísmo de bajo nivel para la sincronización de procesos – Operaciones – Implementación – Ejemplos: Filósofos, Lectores-Escritores – Limitaciones • Monitor: mecanísmo de alto nivel para la sincronización de procesos M. B. Ibáñez M. B. Ibáñez Requerimientos para los Mecanismos de Sincronización Tipo de Dato Semáforo • Deben permitir tan solo un poceso a la vez dentro de una región crítica • Si se realizan varias peticiones a la vez, solo un proceso puede tener acceso a la región crítica • Si varios procesos esperan, cada uno de ellos entrará eventualmente a la región crítica • No usar los recursos mientras se está dentro de la región crítica • Debe ser fácil de usar WAIT(semaphore) Interface operators SIGNAL(semaphore) M. B. Ibáñez Uso de los Semáforos Exclusión Mutua var s: semaphore; s.count = s.count - 1 if s.count < 0 then { coloque el proceso en s.queue bloquee este proceso } SIGNAL(s) s.count = s.count + 1 if s.count ≤ 0 then { remueva el proceso de s.queue coloque el proceso en la cola “ready” } count Queue pointer M. B. Ibáñez Semántica de las Operaciones WAIT(s) Representation of a semaphore var data-structure: some-type; lock: semaphore = 1; procedure access-shared-data { <non-critical instructions> WAIT(lock); <access shared-data-structure> SIGNAL(lock); <non-critical instructions> } M. B. Ibáñez M. B. Ibáñez 1 Uso de los Semáforos Uso de los Semáforos Sincronización de Procesos Cooperantes Múltiples instancias de un recurso Sean los procesos P1 = {S1} y P2 = {S2} Se quiere ejecutar S2 solo después de haber concluido S1 var synch: semaphore = 0; procedure P1 {S1; signal(synch)} procedure P2 {wait(synch); S2} var s: semaphore = N; procedure usar-recurso{ wait(s);} procedure liberar-recurso{ signal(s);} M. B. Ibáñez M. B. Ibáñez Uso de los Semáforos Buffer de capacidad limitada I Buffer de capacidad limitada II Producer: Crea items Consumer: Utiliza (destruye) items Buffer: Capacidad para N items • Solo un proceso a la vez puede usar el buffer • Los consumidores no pueden tomar items del buffer cuando éste está vacío • Los productores no pueden añadir items al buffer cuando éste está lleno M. B. Ibáñez El productor añade items al buffer var empty_spaces: semaphore = N; procedure producer { while(true){ produce_item(); wait(empty_spaces); put_item_into_buffer(); } } procedure consumer { while(true) { consume_item(); signal(empty_spaces); } } M. B. Ibáñez Buffer de capacidad limitada III Buffer de capacidad limitada IV El consumidor toma items del buffer Productor y Consumidor comparten el buffer var full_spaces: semaphore = 0; procedure producer { while(true){ produce_item(); signal(full_spaces); } } procedure consumer { while(true) { wait(full_spaces); consume_item(); } } M. B. Ibáñez var mutex: semaphore = 1; procedure producer { while(true){ produce_item(); wait(mutex); put_item_into_buffer(); signal(mutex); } } procedure consumer { while(true) { wait(mutex); take_item_from_buffer(); signal(mutex); consume_item(); } } M. B. Ibáñez 2 Buffer de capacidad limitada V El problema de los filósofos procedure consumer { while(true) { wait(full_spaces); wait(mutex); take_item_from_buffer(); signal(mutex); signal(empty_spaces); consume_item(); } } procedure producer { while(true){ produce_item(); wait(empty_spaces); wait(mutex); put_item_into_buffer(); signal(mutex); signal(full_spaces); } } M. B. Ibáñez M. B. Ibáñez Primer intento Definiciones var fork: array[0..4] of semaphore; /* all elements = 1 */ procedure philosopher(i:integer) repeat wait(fork[i]); /* takes left fork */ wait(fork[i+1 mod 5]); /* takes right fork */ <eat> signal (fork[i]); /* releases left fork */ signal (fork[i+1 mod 5]); /* releases right fork */ <think> until false; # define N 5 # define LEFTN (i-1)%N # define RIGHTN (i+1)%N /* Número de filósofos */ /*Vecino izquierdo del filósofo i */ /*Vecino derecho del filósofo i*/ # define THINKING 0 # define HUNGRY 1 # define EATING 2 /* Estado */ /* Estado */ /* Estado */ M. B. Ibáñez M. B. Ibáñez Semáforos Acciones de los Filósofos var state: integer array[N]; mutex: semaphore = 1; s: semaphore array[N]; /*estado de cada filósofo*/ /* examine el estado de un solo filósofo cada vez */ /* si un filósofo no logra tomar los tenedores, se autobloquea */ M. B. Ibáñez procedure philosopher(i: integer){ while (true) { state[i] = THINKING; get_forks(i); state[i] = EATING; release_forks(i); } M. B. Ibáñez } 3 procedure get_forks(i: integer) procedure release_forks(i: integer) { wait(mutex); /* Nadie más puede trabajar sobre state */ state[i]= HUNGRY; /* Quiere los recursos*/ test(i); /* trata de obtener ambos tenedores */ signal(mutex); wait(s[i]); /* se bloquea si no obtiene los tendores */ } { /* Nadie más puede ttrabajar sobre state */ wait(mutex); state[i]= THINKING; /* Ya no necesita los recursos */ test(LEFT); /* su vecino izquierdo puede comer? */ test(RIGHT); /* su vecino derecho puede comer? */ signal(mutex); } M. B. Ibáñez M. B. Ibáñez procedure test(i: integer) Lectores-Escritores {if (state[i] == HUNGRY and state[LEFT] != EATING and state[RIGHT] != EATING ) { state[i] = EATING; signal(s[i]); } } • Un objeto es compartido por varios procesos concurrentes • Los lectores solo leen el contenido del aobjeto compartido • Los escritores actualizan el objeto compartido M. B. Ibáñez M. B. Ibáñez Garantizando la exclusión mutua Variables var access_db: semaphore = 1; procedure reader {…} procedure writer{ … wait(access_db); <writing is performed>; signal(access_db); ... } M. B. Ibáñez var access_counter: semaphore = 1; access_db: semaphore = 1; n_readers: integer = 0; M. B. Ibáñez 4 procedure reader procedure reader {while(true){ ¿Soy el primer lector? SI: wait(access_db) READ ¿Soy el último lector? NO: signal(access_db) USE DATA } {while(true){ wait(access_counter); n_readers = n_readers + 1; /* first reader ? */ if (n_readers == 1) then wait(access_db); signal(access_counter); < read data base > ; M. B. Ibáñez M. B. Ibáñez Limitaciones de los Semáforos Monitor: Estructura de Datos I t yp e monitor-name = mon it or • Es fácil cometer errores. – No es sencillo recordar qué semáforo está asociado a una determinada estructura de datos. – Sería preferible tener una alternativa. • El tiempo durante el cual un proceso se bloquea no está limitado. – Un proceso está bloqueado hasta que alguien lo depierta gracias a una señal. • procedure entry P1(…) {…}; procedure entry P2(…) {…}; ... procedure entry PN(…) {…}; { < initialization-code > } O1 P1 O2 P2 shared data PN M. B. Ibáñez M. B. Ibáñez Monitor: Estructura de Datos II Variables de Condición Solo un proceso a la vez puede estar activo dentro del monitor Un procedimiento definido dentro de un monitor puede tener acceso a: – Sus parámetros formales – Sus variables locales – Las variables del monitor • Monitor <variable-declar ations>; • Cuando se examina un recurso y éste está ocupado el proceso siempre se bloquea • wait(access_counter); n_readers = n_readers - 1; /* last reader ? */ if (n_readers == 0) then signal(access_db); signal(access_counter); < use data >; }} Las variables locales de un monitor pueden ser utilizadas solo por los procedimientos locales Monitor P1 O1 O2 process1 process1 P2 process1 shared data PN M. B. Ibáñez var x,y: condition; • El proceso que invoca “x.wait;” es suspendido hasta que otro proceso invoque “x.signal;” • La “x.signal;” reasume exactamente un proceso suspendido • Si ningún proceso está suspendido, la señal no tiene efecto. Monitor P1 O1 O2 process1 process2 P2 shared data X process3 Y PN process 4 process 5 process 6 M. B. Ibáñez 5 Ejemplo:Asignación de Recursos I type resource-assignation = monitor var busy: boolean; var free: condition; procedure entry reserve(){...} procedure entry release(){…} { busy := false; } Ejemplo: Asignación de Recursos II procedure entry reserve() { if busy then free.wait; busy := true; } process use_resource(){ resourceassignation.reserve(); using_resource(); procedure entry release() { busy := false; free.signal; }; resourceassignation.release(); } M. B. Ibáñez M. B. Ibáñez Solución al problema de los filósofos utilizando Monitores I Solución al problema de los filósofos utilizando Monitores II type dining-philosophers = monitor var state: array[0..4] of {thinking, hungry, eating}; var self: array[0..4] of condition; procedure entry pickup(i: 0..4);{…} procedure entry putdown(i: 0..4);{…} procedure entry test(k: 0..4);{…} { for i := 0 to 4 do state[i] := thinking; } M. B. Ibáñez procedure entry pickup(i: 0..4); { state[i] := hungry; test(i); if state[i] != eating then self[i].wait; } procedure entry putdown(i: 0..4); { state[i] := thinking; test(i+4 mod 5); test(i+1 mod 5); } M. B. Ibáñez Solución al problema de los filósofos utilizando Monitores III procedure test(k:0..4); { if state[k+4 mod 5] != eating and state[k] = hungry and state[k+1 mod 5] != eating then { state[k] := eating; self[k].signal; } } M. B. Ibáñez 6