Concurrencia: Exclusión Mútua y Sincronización

Post on 03-Jan-2016

62 views 2 download

description

Concurrencia: Exclusión Mútua y Sincronización. Capítulo 5. Concurrencia. Concurrencia abarca conceptos tales como Comunicación entre procesos Compartición y competición de recursos compartidos Sincronización de actividades de multiples procesos colaborativos - PowerPoint PPT Presentation

Transcript of Concurrencia: Exclusión Mútua y Sincronización

1

Concurrencia: Exclusión Mútua y Sincronización

Capítulo 5

2

Concurrencia

• Concurrencia abarca conceptos tales como– Comunicación entre procesos

– Compartición y competición de recursos compartidos

– Sincronización de actividades de multiples procesos colaborativos

– Asignación de la CPU a los procesos

• Concurrencia no sólo surge en sistemas multiprocesadores o sistemas distribuidos, sino también en sistemas monoprocesadores con multiprogramación

3

Concurrencia

4

Principios de concurrencia

• No se puede asumir la velocidad relativa y el orden de ejecución de los procesos, la cual depende de– Las actividades de otros procesos o hebras

– El manejo de interrupciones

– La política de scheduling del SO

• Surgen problemas como – Compartición de recursos globales

– Administración eficiente de recursos. ¿Qué pasaría si el SO asigna un recurso de I/O a un proceso y éste inmediatamente se suspende?

– Difícil encontrar errores de programación. Los errores de sincronización y concurrencia son típicamente poco determinísticos.

5

Un ejemplo simple

void echo(){

chin = getchar(); // asuma que chin is compartidachout = chin;putchar(chout);

}

Proceso P1 Proceso P2. .chin = getchar(); .. interrupción chin = getchar();chout = chin; chout = chin;putchar(chout); . interrupción. putchar(chout);. .

• Solución: Permitir que sólo un proceso ejecute la función a la vez

6

Condición de carrera (race condition)

• Una condición de carrera ocurre cuando varios procesos o hebras leen y escriben datos compartidos y el resultado final depende del orden en que se ejecuten las instrucciones

• Ejemplo: Inicialmente, b=1, c=2, variables compartidas

P1 P2

b = b + c c = b + c

7

Interacción entre Procesos

• Procesos no tienen conocimiento de la existencia de otros procesos – Aunque los procesos no trabajen colaborativamente, el SO debe administrar la competencia por recursos del sistema

• Procesos conocen indirectamente la existencia de otros – Saben que los recursos que accesan son compartidos con otros procesos

• Procesos que directamente conocen la existencia de otros procesos– Conocen el pid de los otros procesos

– Se comunican explícitamente

– Cooperan en alguna tarea

8

9

Cocurrencia en competencia de recursos

• Dos o más procesos necesitan accesar un (o más) recurso durante sus ejecuciones.

• Los procesos no saben de la existencia de otros procesos y sus operaciones no deben ser afectadas por la ejecución de otros procesos.

• Entonces, los procesos no deben afectar el estado de los recursos.

• Problemas potenciales de control– Exclusion mútua - sectiones críticas

• Sólo un programa a la vez puede ejecutar su sección crítica

– Deadlock (abrazo mortal)• P1 solicita y obtiene recurso R1; P2 solicita y obtiene R2. Luego P1 solicita R2, y P2 solicita R1

– Starvation (inhanición) • Es posible que un proceso esperando por un recurso (bloqueado) nunca lo obtenga

10

Concurrencia en compartición

• Ejemplo: Múltiples procesos acceden variables compartidas

• Datos compartidos pueden ser leídos o escritos, y las operaciones de escritura debieran ser exclusivas

• Otro problema coherencia de datos

• Ejemplo: suponga que necesitamos mantener a=b

P1 P2

a = a+1 b = 2*b

b = b+1 a = 2*a

11

Concurrencia en cooperación mediante comunicación

• Varios procesos trabajan en conjunto para resolver alguna tarea

• Los procesos se comunican explícitamente, por ejemplo mediante el envío de mensajes

• Si toda la comunicación está basada en paso de mensajes, no existe problema de exclusión mútua.

• Sin embargo, puede existir deadlock e inhanición

12

Exclusión mútua

• Una visión abstracta de la implementación de EM sería

P1 P2 void P1() void P2(){ { while (true) { while (true) { código antes de la SC código antes de la SC entercritical(Ra); entercritical(Ra); SC; SC; exitcritical(Ra); exitcritical(Ra); código después de la SC código después de la SC } }} }

13

Requirimientos para exclusión mútua

• Sólo un proceso puede estar ejecutando su SC

• Un proceso que no se encuentra en su SC no debe interferir con otros procesos que deseen ingresar a la SC

• No se permite que un proceso que desea entrar en su SC espere indefinidamente: No deadlock o inhanición

• Cuando ningún proceso está en su SC, la solicitud de cualquier proceso para ingresar a su SC no debe ser denegada

• No se puede asumir nada respecto de la velocidad relativa y orden en la ejecución de los procesos

• Un proceso permanece en su SC por un tiempo finito

14

Exclusión mútua:solución por hardware

• Deshabilitación de Interrupciones– Recordar que un proceso se ejecuta hasta que invoca un llamado al SO o es

interrumpido– Entoces para garantizar EM, el proceso deshabilita las interrupciones justo antes

de entrar en su SC while (true)

{ deshabilitar_interrupciones(); SC habilitar_interrupciones(); }

– Problema 1: habilidad del procesador para hacer context-switch queda limitada– Problema 2: no sirve para multiprocesadores

15

EM: solución por hardware

• Instrucciones especiales de máquina – 2 acciones se llevan a cabo en forma atómica, como leer y escribir o leer y probar sobre

una dirección simple de memoria

– Acceso es bloqueado para cualquier otra instrucción

• Test and Set Instruction

boolean testset (int i) {

if (i == 0) {

i = 1;

return true;

}

else {

return false;

}

}

16

EM: solución por hardware

• Instrucción Exchange (swap)

void exchange(int register, int memory) {

int temp;

temp = memory;

memory = register;

register = temp;

}

17

EM: ejemplo

• parbegin(P(1), P(2),…,P(n)) suspende la ejecución del programa principal e inicia la ejecución concurrente de n procesos con el mismo código, pero diferente parámetro i

• Sólo aquel que encuentra bolt=0 entra. Los otros permanecen en busy-waiting o spin-waiting

18

EM por instrucciones de máquina

• Ventajas– Aplicable a cualquier número de procesos y procesadores que comparten

memoria física

– Simple y fácil de verificar

– Puede ser usada con varias SC, cada una controlada por su propia variable

• Desventajas– Busy-waiting consume tiempo de procesador

– Es posible inhanición

– Es posible deadlock si existe prioridades entre los procesos: Suponga dos procesos P1 y P2, con P1 prioridad más baja que P2. Si P1 está en su SC y luego es interrumpido para que P2 entre al procesador, ¿Qué pasaría?

19

Soluciones por Software

• Dos procesos, P0 y P1 comparten la variable global turn la cual indica cuál de los procesos puede ingresar a la seción crítica

• Inicialmente turn = 0

• Garantiza exclusión mútua• Problemas

– Alternación estricta de ejecución. – ¿Qué pasaría se P0 falla?– No existe inanición

while (true) { while (turn != 0); sc(); turn = 1; otro_codigo… }

while (true) { while (turn != 1); sc(); turn = 0; otro_codigo… }

P0 P1

20

Segundo intento

• El problema con la solución anterior es que almacenamos el nombre del proceso que puede entrar a la SC

• Una nueva posible solución es almacenar el nombre del procesos que está en su SC

• Boolean flag[2]

• Incialmente flag[0] = flag[1] = false

• Si el uno de los procesos falla fuera de su SC, el otro no se bloquea indefinidamente, pero si se bloque dentro de su SC, si.

• Esta solución no satisface el requerimiento de exclusión mútua!!!

while (true) { while (flag[1]); flag[0] = true; sc(); flag[0] = false; otro_codigo… }

while (true) { while (flag[0]); flag[1] = true; sc(); flag[1] = false; otro_codigo… }

21

Tercer intento

• El problema más evidente de la solución anterior es que un proceso puede cambiar su estado después que el otro proceso la ha “consultado”, pero antes que el otro haya entrado a su SC

• flag[0] = flag[1] = False

• El requerimiento de exclusión mútua es satisfecho, pero…

• Se puede producir deadlock ¿por qué?

while (true) { flag[0] = true; while (flag[1]); sc(); flag[0] = false; otro_codigo… }

while (true) { flag[1] = true; while (flag[0]); sc(); flag[1] = false; otro_codigo… }

22

Cuarta solución

• Considere la siguiente secuencia– P0 pone flag[0] en true– P1 pone flag[1] en true– P0 chequea flag[1]– P1 chequea flag[0]– P0 pone flag[0] en false– P1 pone flag[1] en false– P0 pone flag[0] en true– P1 pone flag[1] en true

while (true) { flag[0] = true; while (flag[1]){ flag[0] = false; flag[0] = true; } sc(); flag[0] = false; otro_código… }

while (true) { flag[1] = true; while (flag[0]){ flag[1] = false; flag[1] = true; } sc(); flag[1] = false; otro_codigo… }

Livelock

23

Una solución correcta (algoritmo de Peterson)

• En este algoritmo usamos dos variables globales:– Turn que indica cual proceso podria entrar a la SC y

– flag[] que indica el desea de entrar a la SC

– Inicialmente flag[0] = flag[1] = false

• Esta solución garantiza exclusión mútua

• No produce deadlock

while (true) { flag[0] = true; turn = 1; while (flag[1] && turn == 1); sc(); flag[0] = false; otro_codigo… }

while (true) { flag[1] = true; turn = 0; while (flag[0] && turn == 0); sc(); flag[1] = false; otro_codigo… }

24

Semáforos

• Una semáforo es una variable especial usada para que dos o más procesos se señalicen mutuamente

• Un semáforo es una variable entera – Inicializada con un número no negativo

– Sólo dos operaciones acceden al semáforo

– semWait(s) decrementa el semáforo; si el valor resultante es negativo, el proceso se bloquea; sino, continúa su ejecución

– semSignal(s) incrementa el semáforo; si el valor resultante es menor o igual que zero, entonces se despierta un proceso que fue bloqueado por semWait()

25

Primitivas sobre semáforos

26

Semáforo binario

• semWait(s) { if (s.value == 1) s.value = 0; else { place this process in s.queue block this process } }

• semSignal(s) { if (s.queue is empty()) s.value = 1 else { remove a process P from s.queue; place P on the ready list; }

• un semáforo binario puede ser inicializado en 0 ó 1

• también son conocidos como mutex

27

Exclusión mútua usando semáforos binarios

28

29

Políticas de encolamiento definequién, entre aquellos que estánesperando, pasa a ejecutarse.

semáforo fuerteGarantiza libre de inhaniciónEj: FIFO

Semáforo débilNo garantiza libre de inhaniciónEj: Prioridad

30

El problema del Productor/Consumidor

• Uno o más productores generan datos y lo colocan en un buffer (compartido)

• Un consumidor toma (consume) los datos del buffer uno a la vez

• Sólo un agente (productor o consumidor) accesa el buffer a la vez

31

Caso buffer infinito

producer:

while (true) {

/* produce item v */

b[in] = v;

in++;

}

consumer:

while (true) {

while (in <= out)

/*do nothing */;

w = b[out];

out++;

/* consume item w */

}

32

Caso buffer circular o finito

producer:

while (true) {

/* produce item v */

while ((in + 1) % n == out)

/* do nothing */;

b[in] = v;

in = (in + 1) % n

}

consumer:

while (true) {

while (in == out)

/* do nothing */;

w = b[out];

out = (out + 1) % n;

/* consume item w */

}

33

34

•Semáforo binario s es usado para asegurar exclusión mútua

•Semáforo delay se usa para hacer que el consumidor espere si el buffer está vacio

• Variable n = in-out

Solución incorrecta

35

Solución correcta

36

Solución caso buffer infinito con semáforo contador

37

Solución caso buffer finitocon semáforo contador

Semáforo e usado paracontar el número de espaciosvacíos

BloquearseProductor desea insertar en buffer llenoConsumidor desea sacar de buffer vacío

DesbloquearseConsumidor: item insertadoProductor: item sacado

38

Implementación de semáforos con testset()

• semWait(s) {

while (!testset(s.flag)) ;

s.count--;

if (s.count < 0) {

place this process in s.queue

block this process and set s.flag to 0

} else (NOOOOO)

s.flag = 0;

• semSignal(s) {

while (!testset(s.flag));

s.count++;

if (s.count <= 0) {

remove a process P from s.queue;

place P on ready list;

}

s.flag = 0;

El SO debe garantizar que la operaciones sobre semáforosse realicen en forma atómica

En este caso la deshabilitación de interrupciones sería una opción válida

39

El problema de los filósofos comensales

• Cinco filósofos se sientan alrededor de una mesa con una fuente de spaghetti.

• Cuando un filósofo no está comiendo, está filosofando.• Como carecen de habilidades manuales, necesitan de dos tenedores para

comer, pero sólo hay cinco.• Cuando uno o dos de los tenedores están siendo ocupados por el filósofo de

al lado, el filósofo vuelve a filosofar.• Cuando un filósofo deja de comer spaghetti, coloca los tenedores sobre la

mesa.

40

Solución 1

• Esta solución no está libre de deadlock

Semaphore fork[5] = {1}void philosopher(int i) { while (true) { think(); semWait(fork[i]); semWait(fork[(i+1)mod 5]); eat(); semSignal(fork[(i+1] mod 5); semSignal(fork[i]); }}

main(){ parbegin(philosopher(0),philosopher(1), philosopher(2),philosopher(3), philosopher(4));

}

41

Solución 2• Una posible solución al problema de deadlock es controlar que sólo 4

tenedores puedan ser usados

Semaphore fork[5] = {1}Semaphore room = 4;void philosopher(int i) { while (true) { think(); semWait(room); semWait(fork[i]); semWait(fork[(i+1)mod 5]); eat(); semSignal(fork[(i+1] mod 5); semSignal(fork[i]); semSignal(room); }}

main(){ parbegin(philosopher(0),philosopher(1), philosopher(2),philosopher(3), philosopher(4));

}

42

El problema de los lectores/escritores

• Uno o más lectores pueden estar simultáneamente leyendo un archivo

• Pero sólo un escritor puede estar escribiendo el archivo

• Si un escritor está escribiendo el archivo, ningún lector puede estar leyendo el archivo

• Ej: sistema de biblioteca

• Note que: se garantiza que los lectores sólo leen

• Si lectores y escritores leyeran y escribieran, volveriamos al problema general de exclusión mútua

43

Lectores tienenPrioridad

Escritores puede entrar en inanición

44

Escritores tienen prioridad,

Escritores tienen prioridad, es decir una vez que un escritor declara su deseo de entrar, no se permite que ningún nuevo lector entre

45

Problema típico con semáforos

• ¿Qué pasaría si el programador se equivoca usando las funciones semWait() y semSignal() en el consumidor?

n = 0s = 1void consumer(){ while (true) { semSignal(s); semWait(s); take(); senWait(n); }}

void producer(){ while (true) { produce(); semWait(s); append(); semSignal(s) senWait(n); }}

46

Monitores

• Un monitor es un módulo de software que evita errores de programación

• Carácterísticas principales:– Variables locales son accesadas sólo dentro del monitor; es decir por la

funciones del monitor

– Para ingresar al monitor, un proceso invoca algunas de las funciones del monitor

– Sólo un proceso puede estar ejecutándose en el monitor => exclusión mútua

• Los monitores necesitan usar variables de condición:– Suponga que un proceso dentro del monitor necesita suspenderse hasta que una

condición se haga verdadera

– Las variables de condición proveen el mecanismo de sincronización para suspender un proceso dentro del monitor y reanudar su ejecución en otro momento

47

Variables de condición

• cwait(c): suspende la ejecución del proceso llamador en la variable de condición c. El monitor queda libre para ser usado por otro proceso

• csignal(c): resume la ejecución de algún proceso bloqueado por un cwait() sobre la misma variable c. Si hay varios procesos bloqueados, elegir cualquiera; si no hay ninguno bloqueado, no hacer nada

• Note que las funciones cwait() y csignal() se parecen a semWait() y semSignal(), PERO NO son iguales y cumplen una función distinta

48

49

Solución al problema productor/consumidor con monitor

notfull v.c. para dormir el productor cuando buffer está llenonotempty v.c. para dormir al consumidor cuando el buffer está vacío

50

Solución (continuación)

51

Solución al problema de los filósofos con monitores

Monitor dinning_philosophersstate[5] of {THINKING, HUNGRY, EATING};Cond self[5];

void get_forks(int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING then cwait(self[i]);}void put_forks(int i) { state[i] = THINKING; test((i+4) mod 5); test((i+1) mod 5);}void test(int i) { if (state[i+4 mod 5] != EATING && state[i] == HUNGRY && state[i+1 mod 5] != EATING) { state[i] = EATING; csignal(self[i]); }}

Void philosopher(int i){ while (true) { think(); get_forks(i); eat(); put_forks(i); }}

void main() { state[0] = state[1] =… THINKING parbegin(philosopher(0),... )}

52

Paso de Mensajes

• Los mecanismos de sincronización que hemos visto hasta ahora están basados en variables compartidas (globales), es decir la comunicación es implícita

• El paso de mensajes es un mecanismo de sincronización donde la comunicación es explícita.

• Paso de mensajes significa que un proceso envía información a otro proceso con el cual no comparten memoria

• Dos primitivas básicas

send (destination, message)

receive (source, message)

53

Sincronización

• El paso de mensajes implica necesariamente un nivel básico de sincronización: el receptor de la información no puede recibirla antes que el enviador la envíe.

• Blocking send, blocking receive– Tanto el enviador como el receptor se bloquean hasta que el mensaje llegue al

receptor

• Nonblocking send, blocking receive– El enviador continúa su ejecución después de enviarla

– El receptor se bloquea hasta que llegue

• Nonblocking send, nonblocking receive– Ninguno se bloquea

54

Direccionamiento

• Direccionamiento directo– La primitiva send() incluye el identificador del proceso destino

– La primitiva receive() podría (o no) saber quien le envía el mensaje y tomaria la información del mensaje fuente

– Asi, podría enviar una confirmación de recepción del mesaje

• Direccionamiento indirecto– Los mensajes no se envia de un proceso a otro, sino a una estructura de datos

compartida consistente de colas que almacenan temporalmente mensajes

– Mailboxes

– Asi, un proceso deposita un mensaje en el mailbox y otro lo recupera

– Permite tener más modelos de comuniucación, como se presenta a continuación

55

56

Formato de mensaje

57

Exclusión mútua con paso de mensajes• Asumimos blocking receive() y nonbloquing send()

• Se crea un “mailbox” mutex

• Para entrar a la SC, un proceso debe recibir un mensaje

• Cuando sale de la SC pone devuelta el mensaje en el mailbox

• Asumimos si dos o más procesos hacen un receive() al mismo tiempo, sólo uno de ellos lo recibe

58

Solución al problema del productor/consumidor