Semaforos

36,171 views

Published on

semaforos

Published in: Technology, Business
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
36,171
On SlideShare
0
From Embeds
0
Number of Embeds
281
Actions
Shares
0
Downloads
920
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Semaforos

  1. 1. Semáforos Cecilia Hernández 2007-1
  2. 2. Semáforos <ul><li>Primitiva de Sincronización propuesta por Dijkstra en 1968 </li></ul><ul><ul><li>Como parte de sistema THE </li></ul></ul><ul><ul><li>Usados para exclusión mutua y planificación </li></ul></ul><ul><ul><li>De nivel más alto que locks </li></ul></ul><ul><li>Variable atómica manipulada por dos operaciones </li></ul><ul><ul><li>Wait(semaforo) </li></ul></ul><ul><ul><ul><li>Decrementa semáforo </li></ul></ul></ul><ul><ul><ul><li>Bloquea hebra/proceso si el semáforo es menor que cero, sino entonces permite a hebra/proceso continuar </li></ul></ul></ul><ul><ul><ul><li>Operacion tambien llamada P(semaforo) o down(semaforo) </li></ul></ul></ul><ul><ul><li>Signal(semáforo) </li></ul></ul><ul><ul><ul><li>Incrementa semáforo en uno y si hay algún proceso/hebra esperando lo despierta </li></ul></ul></ul><ul><ul><ul><li>También llamada V(semaforo) o up(semaforo) </li></ul></ul></ul><ul><ul><li>Valor de semáforo puede ser mayor que 1 </li></ul></ul><ul><ul><ul><li>Inicializado en 1 es como lock. </li></ul></ul></ul><ul><ul><ul><ul><li>Usado para exclusión mutua </li></ul></ul></ul></ul><ul><ul><ul><li>Inicializado en N </li></ul></ul></ul><ul><ul><ul><ul><li>Usado como contador atómico </li></ul></ul></ul></ul>
  3. 3. Implementación de Semáforos typedef struct { int value; struct hebra *L; } semaphore; void wait(semaphore S) { S.value--; if (S.value < 0){ agregar hebra a S.L; block(); } } void signal(semaphore S){ S.value++; if (S.value <= 0){ remover hebra T de S.L; wakeup(T); } }
  4. 4. Exclusión mutua vs planificación Sección crítica lock unlock <ul><li>Exclusión mutua </li></ul><ul><li>Sólo una hebra a </li></ul><ul><li>la vez en SC </li></ul><ul><li>- Puede ser cualquier hebra </li></ul><ul><li>Planificación </li></ul><ul><li>Requerimiento de </li></ul><ul><li>orden en ejecución de hebras. </li></ul>tiempo
  5. 5. Ejemplo planificación <ul><li>sem S1 = 0, S2 = 0 </li></ul><ul><li>H1: H2: H3: </li></ul><ul><li>print A; wait(S1); wait(S2); </li></ul><ul><li>signal(S1); print B; print C; </li></ul><ul><li>signal(S2); </li></ul>tiempo H1. imprime A H2. imprime B H3. imprime C
  6. 6. Tipos de Semáforos <ul><li>Binarios (mutex) </li></ul><ul><ul><li>Garantizan exclusión mutua a recurso </li></ul></ul><ul><ul><li>Sólo una hebra/proceso puede accesar sección crítica a la vez </li></ul></ul><ul><ul><li>Contador de semáforo inicializado en 1 </li></ul></ul><ul><li>Contadores </li></ul><ul><ul><li>Representan recursos con más de una unidad disponible </li></ul></ul><ul><ul><li>Permiten accesar recursos de acuerdo al número de recursos disponibles </li></ul></ul><ul><ul><li>Contador es inicializado en N, donde N es la cantidad de unidades disponibles del recurso </li></ul></ul>
  7. 7. Ejemplos Clásicos de Sincronización <ul><li>Problema Productor/Consumidor </li></ul><ul><ul><li>Un buffer en memoria con N slots disponibles </li></ul></ul><ul><ul><ul><li>Necesita llevar cuenta de ítemes en buffer </li></ul></ul></ul><ul><ul><li>Productor produce ítemes a ingresar al buffer </li></ul></ul><ul><ul><li>Consumidor consume ítemes del buffer </li></ul></ul>P C out in Productor Agrega item usando puntero in Consumidor Remueve item usando puntero out
  8. 8. Algoritmo Productor/Consumidor int contador = 0; //indica número de items en buffer Tipo buffer[N]; int in = 0; int out = 0; Productor while (true) { /* produce un item en proxProd */ while (contador == N); //espera buffer[in] = proxProd; in = (in + 1) % N; contador++; } Consumidor while (true) { while (contador == 0); //espera proxCons = buffer[out]; out = (out + 1) % N; contador--; /* consume prodCons */ }
  9. 9. Cómo resolver problema? <ul><li>Identificar restricciones inherentes al problema </li></ul><ul><ul><li>Estado compartido? </li></ul></ul><ul><ul><ul><li>contador (consumidores y productores) </li></ul></ul></ul><ul><ul><ul><li>Buffer </li></ul></ul></ul><ul><ul><ul><ul><li>in ( productores) . Productores no pueden insertar en buffer lleno </li></ul></ul></ul></ul><ul><ul><ul><ul><li>out ( consumidores). Consumidores no pueden extraer de buffer vacío </li></ul></ul></ul></ul><ul><li>Posible resolver con locks? Si </li></ul><ul><li>Posible resolver con semáforos? Si </li></ul>
  10. 10. Cómo resolver problema? <ul><li>Identificar estado compartido y restricciones de problema </li></ul><ul><ul><li>Buffer de tamaño limitado compartido entre productores y consumidores </li></ul></ul><ul><ul><li>Productor escribe en buffer[in], in indica posición de escritura en buffer </li></ul></ul><ul><ul><li>Consumidor extrae de buffer[out], out indica posición de extracción de buffer </li></ul></ul><ul><ul><li>Contador indica el número de elementos actuales en el buffer </li></ul></ul><ul><ul><li>Múltiples productores deben manipular in, buffer[in] y contador atómicamente. </li></ul></ul><ul><ul><li>Múltiples consumidores deben manipular out, buffer[out] y contador atómicamente </li></ul></ul><ul><ul><li>Múltiples consumidores y productores deben manejar contador atómicamente </li></ul></ul>
  11. 11. Solución Productor/Consumidor usando locks Productor while (true) { /* produce un item en proxProd */ lock(mutex); while(contador == N){ unlock(mutex); yield(); } buffer[in] = proxProd; in = (in + 1) % N; contador++; unlock(lock); } Consumidor While(true){ lock(mutex); while(contador == 0){ unlock(mutex); yield(); } proxCons = buffer[out]; out = (out + 1) % N; contador--; unlock(mutex); /* consume proxCons */ } int contador = 0; //indica número de items en buffer char buffer[N]; int in = 0; int out = 0; lock_t mutex;
  12. 12. Solución usando semáforos <ul><li>Identificar estado compartido y restricciones de problema </li></ul><ul><ul><li>Ya presentadas </li></ul></ul><ul><li>Especificar condiciones de espera y señalización </li></ul><ul><ul><li>Cuando buffer está lleno productores deben esperar a que exista una posición vacía (generada por un consumidor) </li></ul></ul><ul><ul><li>Cuando buffer esta vacío consumidores deben esperar a que exista un elemento en el buffer (generado por un productor) </li></ul></ul><ul><ul><li>Acceso a buffer y contador debe realizarse atómicamente </li></ul></ul><ul><li>Identificar semáforos para proveer sincronización </li></ul><ul><ul><li>Mutex (inicializado en 1): para exclusión mutua de buffer, in, out y contador. </li></ul></ul><ul><ul><li>Full (inicializado en 0). Para indicar cuántas posiciones llenas hay en el buffer </li></ul></ul><ul><ul><li>Empty (inicializado en N). Para indicar cuantas posiciones vacías hay en el buffer </li></ul></ul><ul><li>Proporcionar algoritmos </li></ul>
  13. 13. Solución usando semáforos Productor while (true) { /* produce un item en proxProd */ wait(vacio); wait(mutex); buffer[in] = proxProd; in = (in + 1) % N; contador++; signal(mutex); signal(lleno); } Consumidor While(true){ wait(lleno); wait(mutex); proxCons = buffer[out]; out = (out + 1) % N; contador--; signal(mutex); signal(vacio); /* consume proxCons */ } int contador = 0; //indica número de items en buffer char buffer[N]; int in = 0; int out = 0; sem mutex=1; sem vacio = N; sem lleno = 0;
  14. 14. Ejemplos <ul><li>Productor/consumidor usando pthreads y locks </li></ul><ul><ul><li>http://www.inf.udec.cl/~chernand/sc/ejemplos/prodconsLocks.C </li></ul></ul><ul><li>Productor/consumidor usando pthreads y semáforos </li></ul><ul><ul><li>http://www.inf.udec.cl/~chernand/sc/ejemplos/prodconsSem.C </li></ul></ul>
  15. 15. Problema lectores/escritor <ul><li>Caso base de datos </li></ul><ul><ul><li>Varios lectores pueden accesar registro datos simultaneamente </li></ul></ul><ul><ul><li>Sólo un escritor puede escribir </li></ul></ul>E L L Registro BD
  16. 16. Cómo resolver problema? <ul><li>Identificar estado compartido y restricciones de problema </li></ul><ul><ul><li>Base de datos compartida </li></ul></ul><ul><ul><ul><li>Mientras haya un lector un escritor no puede accesar base de datos </li></ul></ul></ul><ul><ul><ul><li>Mientras exista un escritor en base de datos ningún otro escritor o lector puede accesarla </li></ul></ul></ul><ul><li>Identificar condiciones de espera y señalización </li></ul><ul><ul><li>Si existe un escritor en BD otro escritor o lector debe esperar </li></ul></ul><ul><ul><li>Cuando un escritor termina debe señalizar escritor o lector que espera </li></ul></ul><ul><ul><li>Si podemos tener varios lectores debemos contarlos, para saber cuando existe uno </li></ul></ul><ul><ul><ul><li>Si hay uno leyendo y llegan otros, otros tambien pueden leer </li></ul></ul></ul><ul><ul><ul><li>Si solo hay uno y sale puede haber un escritor esperando accesar BD </li></ul></ul></ul><ul><li>Qué semáforos necesitamos </li></ul><ul><ul><li>Uno inicializado en 1 como mutex para manejar contador de lectores </li></ul></ul><ul><ul><li>Uno tipo mutex para escritor y primer lector </li></ul></ul>
  17. 17. Algoritmo usando semáforos <ul><li>sem mutex=1; </li></ul><ul><li>sem escribir = 1; </li></ul><ul><li>Int contadorLectores = 0; </li></ul><ul><li>Escritor: </li></ul><ul><li>wait(escribir); espera por escritor o lector </li></ul><ul><li> Escritor_escribe; Escribe objeto </li></ul><ul><li>signal(escribir); permite leer y/o escribir a otros, escritura completada </li></ul><ul><li>Lector: </li></ul><ul><li>wait(mutex); asegura acceso exclusivo a contador de lectores </li></ul><ul><li>contadorLectores = contadorLectores++; incrementa lectores </li></ul><ul><li>if(contadorLectores == 1) then wait(escribir); Si es el primer lector espera si hay escritor </li></ul><ul><li>signal(mutex); </li></ul><ul><li>Lector_lee; </li></ul><ul><li>wait(mutex); asegura acceso exclusivo a contador de lectores </li></ul><ul><li>contadorLectores = contadorLectores--; lector terminó de leer </li></ul><ul><li>if(contadorLectores == 0) then signal(escribir); no mas lectores por si escritor esperaba </li></ul><ul><li>signal(mutex) </li></ul>
  18. 18. Notas sobre Lectores/Escritores <ul><li>Primer lector se bloquea si hay un escritor activo </li></ul><ul><ul><li>cualquier otro escritor se bloquea también </li></ul></ul><ul><li>Si un escritor espera porque existen lectores activos, el último lector lo despierta cuando sale </li></ul><ul><ul><li>pueden otros lectores entrar cuando el escritor está esperando? </li></ul></ul><ul><li>Cuando un escritor sale, si hay un escritor y un lector esperando quien entra? </li></ul>
  19. 19. Otro ejemplo clásico <ul><li>Problema de Filósofos comensales </li></ul><ul><ul><li>Cada filósofo tiene su plato de arroz, con 5 palitos </li></ul></ul><ul><ul><li>5 filósofos se sientan a la mesa. Piensan por un rato y cuando les da hambre comen </li></ul></ul><ul><ul><li>Hay sólo 5 palitos en la mesa (cada persona necesita 2 palitos para comer arroz a la manera china) </li></ul></ul><ul><ul><li>Para poder comer cada filósofo tiene que obligatoriamente conseguir dos palitos </li></ul></ul><ul><li>Problema es importante porque introduce posibles problemas de Deadlock(bloqueo mortal) y Starvation(inanición) </li></ul>
  20. 20. Problema de Filósofos comensales
  21. 21. Problemas que pueden surgir con mala sincronización <ul><li>Deadlock </li></ul><ul><ul><li>Hebras/Procesos están en deadlock cuando </li></ul></ul><ul><ul><ul><li>2 o más hebras o procesos están esperando por una condición que sólo puede ser causada por una hebra que tambien está esperando. </li></ul></ul></ul><ul><ul><ul><li>Puede darse con 2 o más hebras en la lista de espera de un mismo semáforo? </li></ul></ul></ul><ul><li>Starvation o espera indefinida </li></ul><ul><ul><li>Hebras/Procesos esperan indefinidamente para poder accesar un recurso. </li></ul></ul><ul><ul><ul><li>Ejemplo, una hebra en la lista de espera de un semáforo de la cual están entrando y saliendo continuamente hebras y la lista de espera de semáforo es LIFO </li></ul></ul></ul>
  22. 22. Ejemplo deadlock con productor/consumidor Productor while (true) { /* produce un item en proxProd */ wait(mutex); wait(vacio); buffer[in] = proxProd; in = (in + 1) % N; contador++; signal(mutex); signal(lleno); } Consumidor While(true){ wait(lleno); wait(mutex); proxCons = buffer[out]; out = (out + 1) % N; contador--; signal(mutex); signal(vacio); /* consume proxCons */ } int contador = 0; //indica número de items en buffer char buffer[N]; int in = 0; int out = 0; sem mutex=1; sem vacio = N; sem lleno = 0; Que sucede aquí?
  23. 23. Problemas con Semáforos <ul><li>A pesar que se pueden usar para resolver cualquier problema de sincronización </li></ul><ul><ul><li>Son variables globales por lo tanto pueden ser accesadas de cualquier hebra directamente </li></ul></ul><ul><ul><ul><li>no es buena técnica de ingeniería de software </li></ul></ul></ul><ul><ul><li>No hay conexión entre el semáforo y el recurso para el cual se quiere controlar acceso </li></ul></ul><ul><ul><li>Usados como mutex (ingreso a sección crítica) y para coordinación (planificación, elección quien tiene acceso al recurso) </li></ul></ul><ul><ul><li>No se puede controlar su uso, no hay garantía que el programador los use adecuadamente (fácil de cometer errores) </li></ul></ul>
  24. 24. Resumen <ul><li>Semáforos primitivas de sincronización de más alto nivel que locks </li></ul><ul><li>No relación entre semáforo y recurso que controla </li></ul><ul><li>Fácil de cometer errores que pueden producir deadlock y starvation </li></ul><ul><ul><li>Importante entender bien problema antes de utilizarlos </li></ul></ul><ul><li>Próxima semana Monitores </li></ul>

×