Tema 3: Concurrencia de procesos

Anuncio
Tema 3: Concurrencia de procesos
Yolanda Blanco Fernández
[email protected]
Concurrencia, Tiempo Real y Paralelismo
•
•
•
•
Concurrencia: Convivencia de un conjunto de procesos en un mismo
ordenador.
Sistemas de tiempo real: Limitados por restricciones temporales críticas.
Para vencerlas, los sistemas de tiempo real suelen ser concurrentes.
Paralelismo: Posibilidad de ejecutar varias sentencias simultáneamente en
un mismo ordenador ⇒ precisan varios procesadores ⇒ concurrencia.
Vamos a trabajar con monoprocesadores.
Curso 2009/2010
Grafos de Precedencia
•
Representación gráfica de un algoritmo en el que se establece el orden de
ejecución de un conjunto de instrucciones y/o datos.
I1
I1
I2
I2
I3
I3
I4
I5
I7
Algoritmo
secuencial
•
•
I6
Algoritmo
concurrente
Los procesos concurrentes de un algoritmo pueden ser independientes o
compartir datos.
Nuestro objetivo: estudiar ambos tipos de relaciones entre procesos
concurrentes, mediante soluciones independientes del procesador y su
algoritmo de planificación.
Curso 2009/2010
Construcciones FORK y JOIN (Longway)
•
Fork <etiqueta> ⇒ Divide el flujo en 2 procesos:
• el que viene después de Fork, y
• el que viene después de la etiqueta.
•
Join <contador >: Espera hasta que acaben los contador procesos que debe
unir antes de seguir la ejecución.
Ejemplo 1:
•
I0
I2
I1
I3
contador = 2;
I0 ;
F ORK L1
I1 ;
GOT O L2 ;
L1 : I2 ;
L2 : JOIN contador;
I3 ;
Curso 2009/2010
Construcciones FORK y JOIN (II)
•
Ejemplo 2:
I0
I1
I2
I4
I3
contador = 3;
I0 ;
F ORK L1 ;
I1 ;
GOT O L2 ;
L1 : F ORK L3 ;
I2 ;
GOT O L2 ;
L3 : I3 ;
L2 : JOIN contador;
I4 ;
•
Problemas:
• El uso de GOT O perjudica la legibilidad del programa.
• No empleado en lenguajes de alto nivel.
• Difícil depuración ⇒ etiquetas.
•
Alternativa: Sentencias COBEGIN y COEND.
Curso 2009/2010
Construcciones COBEGIN y COEND (Dijkstra)
•
•
•
También denominadas PARBEGIN y PAREND.
COEND cierra tantas ramas como haya abierto COBEGIN.
Ejemplo:
I
I0 ;
COBEGIN
I1 , I2 , . . . , IN ;
I
I
I
COEN D;
IN +1 ;
I
0
1
2
N
N+1
•
Ejercicio #1: Implementar el siguiente grafo con: (i) sentencias FORK y JOIN
y (ii) sentencias COBEGIN y COEND.
S1
S2
S3
S4
S5
S6
S7
Curso 2009/2010
Construcciones COBEGIN y COEND (II)
•
•
Fácil incorporación en lenguajes de alto nivel. Depuración más sencilla que
FORK y JOIN.
¿Equivalencia entre FORK-JOIN y COBEGIN-COEND?
• Ejercicio #2: Escribir el siguiente grafo con sentencias FORK-JOIN y
COBEGIN-COEND.
S1
S2
S3
S4
S5
S6
S7
• NO son equivalentes.
• En UNIX: fork(), execl(), wait() y waitpid(PID).
Curso 2009/2010
Procesos Concurrentes que comparten Datos
•
•
Al compartir datos entre procesos se pueden producir problemas de
indeterminismo (resultados diferentes según escenario de prueba).
Ejemplo: S1 y S2 no son independientes, sino que comparten la variable x.
S0
S2
S1
S3
S0 : x = 100;
S1 : x := x + 10;
S2 : if x > 100 then write(x);
else write (x − 50);
S3 : nop;
•
•
•
Escenario #1: S1 y S2 ⇒ Se escribe x = 110.
Escenario #2: S2 y S1 ⇒ Se escribe x = 50.
Escenario #3: S2 pierde el control (p. ej. fin de quantum) antes de escribir y
sigue S1 ⇒ Se escribe x = 60.
•
Se han propuesto múltiples soluciones para tratar situaciones
indeterministas con procesos concurrentes compartiendo datos.
Curso 2009/2010
Solución de Bernstein
•
Cada proceso Pi está asociado a dos conjuntos:
• R(Pi ): conjunto de variables accedidas durante la ejecución de Pi .
• W (Pi ): conjunto de variables modificadas durante la ejecución de Pi .
•
Bernstein concluyó que para que dos procesos Pi y Pj , concurrentes,
puedan ejecutarse de forma determinista tienen que satisfacerse las
siguientes condiciones:
• R(Pi ) ∩ W (Pj ) = ∅
• W (Pi ) ∩ R(Pj ) = ∅
• W (Pi ) ∩ W (Pj ) = ∅
•
Condiciones suficientes pero no necesarias ⇒ sólo se pueden compartir
variables de lectura.
•
Objetivo: Buscar soluciones al indeterminismo sin que se cumplan las
condiciones de Bernstein ⇒ compartir variables de escritura.
Curso 2009/2010
Sección Crítica
•
•
•
•
•
•
Ejemplo de motivación: contar el número de coches que pasan por los dos
carriles de una autopista.
El problema surge cuando los procesos-contadores intentan acceder a una
variable compartida.
Sección crítica: zona de código de un proceso en la cual se lee y/o
modifican las variables compartidas por los procesos concurrentes.
Solución: exclusión mutua ⇒ cuando un proceso esté en su sección crítica,
ningún otro puede estar en la suya.
Para ello, adoptaremos las restricciones de Djkstra:
• La solución debe ser independiente del HW o del número de procesos.
• No se puede realizar ninguna suposición sobre la velocidad relativa de
los procesos.
• Cuando un proceso está fuera de su sección crítica no puede impedir a
otros procesos entrar en sus respectivas secciones críticas.
• La selección del proceso que debe entrar en la sección crítica no puede
posponerse indefinidamente.
Puede producirse interbloqueo: procesos bloqueados que sólo podrían ser
desbloqueados por otros que también están en bloqueo.
Curso 2009/2010
Posibles Soluciones al Contador de Coches
1.
Asociar una variable booleana libre al acceso al recurso común (la sección
crítica). El proceso accede si libre=TRUE.
• No sirve porque si se pierde el control en libre=FALSE, los dos procesos
accederán a la vez al recurso ⇒ indeterminismo!!
2.
Un proceso pasa cuando libre=TRUE y el otro cuando libre=FALSE.
• Sólo funciona si la velocidad de ambos procesos está acompasada: si
pasan dos coches por el carril izquierdo, sólo se podrá contar el segundo
cuando pase un coche por el carril derecho (y ponga libre=TRUE).
• Solución inválida por violar la tercera restricción de Djkstra: un proceso
impide que otro acceda a la sección crítica cuando no la está usando.
3.
Si el segundo proceso quiere acceder a la sección crítica, le dejamos; en
caso contrario, accede el primer proceso.
• Se garantiza exclusión mutua.
• Es posible que los dos procesos se den el turno el uno al otro y ninguno
acceda al recurso común ⇒ se viola 4a restricción Djkstra.
4.
La solución válida es el algoritmo de Dekker.
Curso 2009/2010
Algoritmo de Dekker
Curso 2009/2010
Conclusiones
•
•
•
•
Las soluciones son mucho más complejas de lo que parecen a simple vista.
Incluso en el algoritmo de Dekker la sincronización se consigue siempre
mediante espera activa (esperar por una condición comprobándola
continuamente) ⇒ despilfarro de recursos.
La programación concurrente tiene que ser sistemática ⇒ Demasiados
escenarios de prueba para ingenieros SW.
Es necesario recurrir a herramientas de programación más potentes ⇒
herramientas de sincronización:
• Herramientas de bajo nivel.
• Herramientas de nivel intermedio: semáforos.
• Herramientas de alto nivel: regiones críticas y monitores.
Curso 2009/2010
Herramientas de Sincronización
•
Funciones primitivas, implementadas de forma SW o HW, que ayudan a
controlar la interacción entre procesos concurrentes:
• Sincronización: Los procesos intercambian señales que controlan su
avance.
• Comunicación: Los procesos intercambian información.
•
Características deseables:
• Desde el punto de vista conceptual:
• Simplicidad.
• Generalidad.
• Verificabilidad.
• Desde el punto de vista de implementación:
• Eficiencia.
•
Tipos de herramientas de sincronización:
• Nivel HW: acceso a recursos HW compartidos asegurando uso en
exclusión mutua (por ejemplo, memoria y buses).
• Nivel SW: LOCK/UNLOCK, TEST-AND-SET, SWAP.
Curso 2009/2010
Primitivas LOCK/UNLOCK, TEST-AND-SET y SWAP
•
•
•
•
•
Deben ejecutarse de forma indivisible.
LOCK bloquea el acceso a la variable común y UNLOCK lo desbloquea.
TEST-AND-SET devuelve el valor de la variable común y la pone a TRUE
(para bloquearla).
SWAP (var a, b: BOOLEAN) intercambia el valor de a por b y de b por a.
Conclusiones:
• Consiguen soluciones más sencillas que la propuesta por Dekker.
• Solución generalizable a N procesos.
• Principal inconveniente: No se elimina la espera activa.
• Las herramientas de sincronización de bajo nivel no se usan en
aplicaciones concurrentes.
• Son la base para las herramientas de alto nivel.
Curso 2009/2010
Los Semáforos
•
•
Objetivo: solución al problema de la exclusión mutua evitando la espera
activa (Dijkstra, 1965).
Un semáforo sem consta de tres partes:
• Una variable entera interna (s) con un valor máximo N (no accesible para
los procesos).
• Una cola de procesos (no accesible para los procesos).
• Dos funciones básicas de acceso:
• wait(sem): si s < 0, el proceso se suspende en la cola asociada al
semáforo.
• signal(sem): si hay algún proceso suspendido en la cola asociada al
semáforo, se despierta al más prioritario; en caso contrario, se
incrementa en una unidad el valor de s (sin superar el valor máximo
N ).
Curso 2009/2010
Implementación de WAIT y SIGNAL
•
WAIT(s):
s := s − 1
if s < 0 then
begin
estado-proceso = espera;
poner-proceso-en-cola_espera_semáforo;
end;
•
SIGNAL(s):
s := s + 1
if s ≤ 0 then
begin
poner-proceso-en-cola_preparados;
estado-proceso = activo;
end;
•
•
Si s ≤ 0 ⇒ se bloquean todos los procesos; si s ≥ 1 ⇒ no exclusión mutua.
El valor inicial y máximo de la variable s determinan la funcionalidad del
semáforo.
Curso 2009/2010
Semáforos de Exclusión Mutua
•
•
Semáforos binarios: la variable interna s sólo puede tomar los valores 0 y 1.
Solución al problema de la exclusión mutua:
• Un semáforo binario con s = 1.
• Antes de acceder a la sección crítica el proceso ejecuta wait(sem).
• Al finalizar la sección crítica el proceso ejecuta signal(sem).
Curso 2009/2010
Semáforos de Paso
•
Permiten implementar grafos de precedencia.
• Para cada punto de sincronización entre dos procesos: semáforo binario
con s = 0.
• El proceso que debe esperar ejecuta wait(sem).
• Al alcanzar un punto de sincronización el otro proceso ejecuta
signal(sem).
Curso 2009/2010
Semáforos Enteros y de Condición
procesos accediendo a la sección crítica:
Semáforo con s = N , siendo N el valor máximo permitido.
Antes de acceder a la sección crítica el proceso ejecuta wait(sem).
Al finalizar la sección crítica el proceso ejecuta signal(sem).
•
N
•
•
•
•
Sincronización entre procesos en función de una variable entera:
• Semáforo cuya variable s está inicializada a N ó 0 (siendo N el valor
máximo).
• El proceso que debe esperar ejecuta wait(sem).
• Al alcanzar un punto de sincronización el otro proceso ejecuta
signal(sem).
• Ejemplo clásico: problema del productor-consumidor (con búfer limitado
e ilimitado).
Curso 2009/2010
Problema del Producto-Consumidor
•
•
•
•
Sección crítica del productor (P) y del consumidor (C): acceso al búfer.
Condiciones: P produce si búfer no lleno y C consume si búfer no vacío.
Solución con búfer ilimitado (prob [búfer lleno] ≈ 0) ⇒ El consumidor sólo
accederá al búfer cuando haya algún dato que consumir.
Solución con búfer limitado ⇒ El productor sólo volcará dato en búfer si hay
sitio y consumidor sólo accederá si hay algún dato que consumir.
Curso 2009/2010
Solución del Producto-Consumidor con Búfer Ilimitado
PROGRAM P-C;
VAR
buffer: ARRAY [N] of datos;
s: SEMAFORO //en exclusión mutua
vacio: SEMAFORO //de condición
BEGIN
s=1;
vacio=0;
COBEGIN
P; C;
COEND
END
PROCEDURE P;
BEGIN
REPEAT
Producir_Dato;
WAIT(s);
Dejar_Dato_en_Buffer;
SIGNAL(s);
SIGNAL(vacio);
FOREVER;
END;
PROCEDURE C;
BEGIN
REPEAT
WAIT(vacio);
WAIT(s);
Extraer_Dato_del_Buffer;
SIGNAL(s);
Consumir_Dato;
FOREVER;
END;
Curso 2009/2010
Solución del Producto-Consumidor con Búfer Limitado (tamaño N)
PROGRAM P-C;
VAR
buffer: ARRAY [N] of datos;
s: SEMAFORO //en exclusión mutua
vacio, lleno: SEMAFORO //de condición
BEGIN
s=1; // exclusión mutua
vacio=0; //de condición
lleno=N; //de condición
COBEGIN
P; C;
COEND
END
PROCEDURE P;
BEGIN
REPEAT
Producir_Dato;
WAIT(lleno);
WAIT(s);
Dejar_Dato_en_Buffer;
SIGNAL(s);
SIGNAL(vacio);
FOREVER;
END;
PROCEDURE C;
BEGIN
REPEAT
WAIT(vacio);
WAIT(s);
Extraer_Dato_del_Buffer;
SIGNAL(s);
SIGNAL(lleno);
Consumir_Dato; Curso 2009/2010
Problema de la Cena de los Filósofos
Arroz
•
•
•
Los filósofos cogen 2 palillos, se echan arroz del plato, comen y dejan los
palillos.
Suponemos que el arroz no se acaba nunca y que cada filósofo sólo puede
coger los palillos de los compañeros que tiene a ambos lados.
Recurso compartido: los palillos.
Curso 2009/2010
Solución ¿válida? al Problema de los Filósofos
PROGRAM CENA-FILOSOFOS;
VAR
palillos: ARRAY [0:4] of SEMAFORO;
BEGIN
for i=0:4 palillos[i]=1; // exclusión mutua
COBEGIN
for i=0:4 FILOSOFO[i];
COEND
END
PROCEDURE FILOSOFO[i];
BEGIN
REPEAT
Pensar;
WAIT(palillos[i]);
WAIT(palillos[i+1 mod 5]);
Servirse_y_comer;
SIGNAL(palillos[i]);
SIGNAL(palillos[i+1 mod 5]);
FOREVER;
END;
• Un bloque de varios WAIT no es indivisible; un solo WAIT sí lo es.
• Solución inválida: 5 filósofos pueden bloquear un solo palillo y ¡nadie come!
• Solución correcta:
• “comedor virtual” con 4 comensales (1 filósofo siempre come).
• Semáforos utilizados: en exclusión mutua (palillos, incializados a 1) y de
condición (comedor, inicializado a 4).
Curso 2009/2010
Conclusiones
•
Ventajas:
• Mecanismo seguro de acceso a recurso compartido mediante
encapsulamiento de las operaciones sobre la variable que controla el
semáforo.
• Consiguen sincronización de procesos concurrentes evitando la espera
activa.
• El programa principal debe inicializar el semáforo (según su uso).
•
Inconvenientes:
• La inicialización es crítica, así como confundir un wait con un signal u
omitir alguno de ellos.
• Resultan programas muy grandes y complejos, difíciles de depurar.
• Soluciones alternativas: regiones críticas y monitores.
Curso 2009/2010
Yolanda Blanco Fernández
[email protected]
Lab. B-203
Curso 2009/2010
Descargar