Contenidos Bibliografía El problema de la sección crítica Semáforos Regiones críticas Programación Concurrente Principles of Concurrent and Distributed Programming Monitores ProgramaciónConcurrente 1 Concepto de semáforo Signal(s). si hay procesos suspendidos, despierta uno; si no, s:=s+1. (V(s)) 3 Semáforos binarios © Alexis Quesada Arencibia – José Miguel Santos Espino Wait(s). si s>0 Æ s:=s-1; si no, suspende al proceso. (P(s)) ProgramaciónConcurrente Un semáforo que pueda tomar cualquier valor no negativo recibe el nombre de semáforo Un semáforo que sólo puede tomar los valores 0 y 1 recibe el nombre de semáforo El semáforo debe tomar un valor inicial positivo La operación signal(s) despierta uno de los procesos bloqueados. La definición de la operación no especifica qué proceso es despertado ProgramaciónConcurrente 4 Todo semáforo, al margen de su implementación, debe cumplir los invariantes: S >= 0 S = Sinicial + #signals - #waits La definición de las operaciones queda igual salvo signa(s) 2 Invariantes del semáforo binario ProgramaciónConcurrente wait(s) y signal(s) son instrucciones atómicas © Alexis Quesada Arencibia – José Miguel Santos Espino general A. Silberschatz, P. Galvin. Addison-Wesley, 1999 Capítulo 6 Propiedades de los semáforos Una variable entera con dos operaciones atómicas: © Alexis Quesada Arencibia – José Miguel Santos Espino M. Ben-Ari. Prentice Hall, 1990 Capítulo 4 Sistemas Operativos © Alexis Quesada Arencibia – José Miguel Santos Espino J. Palma, C. Garrido, F. Sánchez, A. Quesada, 2003 Capítulo 4 #signals es la cantidad de signal ejecutados en S #waits es la cantidad de waits completados en S Si hay procesos suspendidos, despierta uno, si no, s:=1 © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 5 © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 6 1 Ejercicios Ejercicios b.-) Resolver diversos problemas de sincronización a.-) Resolver el problema de la sección crítica para n procesos con semáforos © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 7 Ejercicios 1.- Introducir las modificaciones necesarias para que d se ejecute solo si e o a se han ejecutado 2.- Introducir las modificaciones necesarias para que d se ejecute solo si e y a se han ejecutado Process P1; begin repeat a; b; forever; end; Process P2; begin repeat c; d; forever; end; © Alexis Quesada Arencibia – José Miguel Santos Espino Process P3; begin repeat e; f; forever; end; ProgramaciónConcurrente 9 B D © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 8 Sin espera activa (1) Espera activa wait(s): loop exit when s>0; end loop; s:=s-1; © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 10 Sin espera activa (2) type semaforo is record valor:integer; L: ListaProceso; end; signal(s): s.valor:=s.valor+1; if s.valor<=0 then L.sacar(proceso); despertar(proceso); end if; © Alexis Quesada Arencibia – José Miguel Santos Espino Implementación type semaforo is record valor:integer; L: ListaProceso; end; wait(s): s.valor:=s.valor-1; if s.valor<0 then L.agregar(proceso); bloquear; end if; C signal(s): s:=s+1; Implementación A Implementación b.2.-) Supongamos tres procesos concurrentes P1, P2 y P3 con el código que se muestra en las figuras. Se pide: b.1.-) Empleando la sentencia concurrente (cobegin, coend) y semáforos, construir un programa concurrente que se corresponda con el siguiente grafo de precedencia ProgramaciónConcurrente wait(s): if s.valor>0 then s.valor:=s.valor-1; else L.agregar(proceso); bloquear; end if; 11 signal(s): if not vacia(L) then L.sacar(proceso); despertar(proceso); else s.valor:=s.valor+1; end if; © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 12 2 Implementación (Nachos) Semaphore::Semaphore(char* debugName, int initialValue) { name = debugName; value = initialValue; queue = new List; } void Semaphore::P() { IntStatus oldLevel = interrupt->SetLevel(IntOff); while (value == 0) { queue->Append((void *)currentThread); currentThread->Sleep(); } value--; (void) interrupt->SetLevel(oldLevel); } © Alexis Quesada Arencibia – José Miguel Santos Espino Implementación Semaphore::~Semaphore() { delete queue; } void Semaphore::V() { Thread *thread; IntStatus oldLevel = interrupt->SetLevel(IntOff); thread = (Thread *)queue->Remove(); if (thread != NULL) scheduler->ReadyToRun(thread); value++; (void) interrupt->SetLevel(oldLevel); } ProgramaciónConcurrente 13 Problemas Aunque entienda perfectamente su uso, un programador puede olvidar accidentalmente alguna de las operaciones (signal o wait) o realizarlas sobre semáforos distintos Uso desordenado de las primitivas podría provocar que no se consiga la exclusión mutua (signal, wait) Bloqueos mutuos El compilador no reconoce qué variables protege un semáforo, con lo cual no nos ayudaría si estamos utilizando una variable compartida fuera de su SC © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 15 Ejercicios NOTA: Las tareas ejecutarán un proceso trivial. Tenemos la sentencia cobegin, coend. El temporizador se puede resolver con un reloj software (un contador que se va decrementando). © Alexis Quesada Arencibia – José Miguel Santos Espino interrupciones Entorno multiprocesador Instrucciones hardware especiales Solución software (espera activa) © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 14 Tanto la exclusión mutua como la condición de sincronización se implementan usando el mismo par de primitivas Difícil identificar el propósito de wait y signal en un código sin mirar el contexto global Código difícil de mantener Código de sincronización repartido entre los diferentes procesos © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 16 Ejercicios Tenemos un sistema con un conjunto de N tareas (procesos): tarea1, tarea2, ...,tareaN. Cada tarea debe ejecutarse periódicamente cada cierto intervalo de tiempo. Por ejemplo: la tarea1 debe ejecutarse cada segundo, la tarea2 cada 10 segundos, etc... Los intervalos de tiempo para cada tarea están predefinidos y se almacenan como datos del programa (por ejemplo en un array). Durantes estos intervalos las tareas están dormidas. Se pide programar un planificador que arranque las tareas cuando les corresponda. Entorno uniprocesador: Operaciones se ejecuten de forma atómica Problemas Mecanismo de bajo nivel: no estructurado Aspecto crítico de los semáforos: ProgramaciónConcurrente 17 En un sistema concurrente existen dos tipos de procesos, A y B, que compiten por utilizar cierto recurso. Al recurso se accede mediante la rutina de solicitarlo, esperar hasta que se pueda usar, usarlo y luego liberarlo. En cualquier momento puede haber un máximo de N procesos de cualquier tipo usando el recurso (N es constante). Por otro lado, para que un proceso de tipo A pueda entrar a emplear el recurso, debe haber al menos el doble de procesos de tipo B que procesos de tipo A dentro del recurso. Diseñe un algoritmo que cumpla con estas reglas de funcionamiento empleando semáforos. © Alexis Quesada Arencibia – José Miguel Santos Espino ProgramaciónConcurrente 18 3