3220_C6_Sincronizaci..

Anuncio
Sistemas Operativos
6 Sincronización de Procesos
Prof. Javier Cañas R.
Nota
•
El texto guía es: Operating System Concepts, Eight Edition,
Avi Silberschatz, Peter Baer Galvin, Greg Gagne
•
Estas PPT están basadas en las PPT originales que el autor
del texto guía mantiene en: http://www.os-book.com/
Copyright Note
The slides below are copyright Silberschatz, Galvin and Gagne, 2008. The slides are authorized for personal use, and for
use in conjunction with a course for which Operating System Concepts is the prescribed text. Instructors are free to modify
the slides to their taste, as long as the modified slides acknowledge the source and the fact that they have been modified.
Paper copies of the slides may be sold strictly at the price of reproduction, to students of courses where the book is the
prescribed text. Any use that differs from the above, and any for profit sale of the slides (in any form) requires the consent
of the copyright owners; contact Avi Silberschatz ([email protected]) to obtain the copyright owners consent.
Temario
1. Conceptos
2. El Problema de la Sección Crítica
3. La Solución de Peterson
4. Sincronización por Hardware
5. Semáforos
6. Problemas Clásicos
... Temario
7. Monitores
8. Sincronización en Linux
9. Sincronización de Pthreads
Objetivos
• Introducir el problema de la sección
crítica cuyas soluciones se utilizan para
asegurar la consistencia de datos
compartidos por varios procesos.
• Presentar soluciones se software y
hardware para solucionarlo.
1 Conceptos
• El acceso concurrente a datos compartidos
puede generar inconsistencia en los datos.
• La mantención de consistencia en los datos
requiere de mecanismos para asegurar la
ejecución ordenada de procesos que
cooperan.
Ejemplo
• Consideremos el problema del Productor
Consumidor con un buffer circular.
Queremos una solución que utilice todas
las entradas del buffer. Podríamos utilizar
una variable count que registre el número
de bufers ocupados. Inicialmente está en
cero. Se incrementa cuando se agrega un
nuevo item y se decrementa cuando se
consume uno.
Solución Capítulo 3
while (true) {
/* Produce an item */
Productor
while (((in + 1) % BUFFER SIZE )
;
== out)
/* do nothing -- no free buffers */
buffer[in] = item;
in = (in + 1) % BUFFER SIZE;
}
while (true) {
while (in == out)
Consumidor
; // do nothing -- nothing to consume
// remove an item from the buffer
item = buffer[out];
out = (out + 1) % BUFFER SIZE;
return item;
}
Usando contador
while (true) {
/*
Productor
produce an item and put in nextProduced
while (count == BUFFER_SIZE)
; // do nothing
buffer [in] = nextProduced;
in = (in + 1) % BUFFER_SIZE;
count++;
}
*/
... Usando contador
while (true)
Consumidor
{
while (count == 0)
; // do nothing
nextConsumed =
out = (out + 1) % BUFFER_SIZE;
}
buffer[out];
count--;
/*
consume the item in nextConsumed
Condiciones de carreras
(race condition)
 count++ could be implemented as
register1 = count
register1 = register1 + 1
count = register1
 count-- could be implemented as
register2 = count
register2 = register2 - 1
count = register2
 Consideremos la ejecución intercalada con “count = 5” inicialmente:
S0: productor ejecuta register1 = count {register1 = 5}
S1: productor ejecuta register1 = register1 + 1 {register1 = 6}
S2: consumidor ejecuta register2 = count {register2 = 5}
S3: consumidor ejecuta register2 = register2 - 1 {register2 = 4}
S4: productor ejecuta count = register1 {count = 6 }
S5: consumidor ejecuta count = register2 {count = 4}
 Se llega al resultado incorrecto count==4
Soluciones al problema
de la Sección Crítica
•
Se deben cumplir 3 condiciones:
1. Exclusión Mutua: Si el proceso Pi se está
ejecutando en su sección crítica, ningún otro
proceso puede estar ejecutándose en su sección
crítica.
2. Progreso: Si ningún proceso está ejecutándose en
su sección crítica y existen procesos que desean
entrar a su sección crítica, entonces, la selección de
los procesos que deben entrar a su sección crítica
después no puede posponerse indefinidamente.
... Soluciones
3. Espera acotada: Debe existir una cota sobre el
número de veces que otro proceso tiene permitido
entrar a su sección crítica después que un proceso
a pedido ingresar a su sección crítica y antes que
sea satisfecha.
•
Adicionalmente:
• Los procesos se ejecutan a velocidades distintas de cero
• No se asume nada respecto a velocidades relativas de los
N procesos
2 El Problema de la
Sección Crítica
•
Cuando un proceso se está ejecutando en su
sección crítica, ningún otro proceso tiene permitido
ejecutarse en su sección crítica.
•
No pueden existir dos procesos en su sección
crítica al mismo tiempo.
do {
entry section
critical section
exit section
!
remainder section
} while (TRUE)
3 La Solución de
Peterson
•
•
Esta solución funciona sólo para dos procesos.
•
Los procesos comparten dos variables:
Se asume que las instrucciones LOAD y STORE
son atómicas, es decir, no pueden ser
interrumpidas.
•
•
int turn; /*indica turno para entrar a sc*/
boolean flag; /* flag[i]=true indica que Pi
está listo para ingresar*/
Algoritmo para
Proceso Pi
do {
! !
! !
flag[i] = TRUE;
turn = j;
! !
while (flag[j] && turn == j);
! !
!
! !
! !
flag[i] = FALSE;
!
remainder section
critical section
! } while (TRUE);
!
4 Sincronización por
HW
• La mayoría de los sistemas proveen
soporte de hardware para proteger
secciones críticas de código.
• Uni procesadores: pueden inhibir las
interrupciones:
•
•
El código que corre, se puede ejecutar sin interrupciones
Muy ineficiente para multiprocesadiores. No es
ampliamente escalable
... HW
• Arquitecturas modernas proveen
instrucciones especiales de máquinas que
tienen atomicidad (no son interrumpibles).
• Las instrucciones más comunes son:
•
Test and set: lee una palabra de memoria y fija un
valor
•
Swap: intercambia el contenido de dos palabreas de
memoria
Solución usando
candados (Locks)
do {
! !
cerrar
!
! !
!
! !
abrir
critical section
remainder section
! } while (TRUE);
! !
!
La instrucción Test and
Set
• Definición:
boolean TestAndSet (boolean *target)
{
boolean rv = *target;
*target = TRUE;
return rv:
}
•
v ← test_and_set(x)
•
El valor de x se copia en v y el valor TRUE se asigna a x dentro del
(IBM/360)
.
mismo ciclo de lectura escritura
Solución usando Test
and Set
• Los procesos comparten la variable booleana
lock que es inicializada FALSE.
• Solución:
do {
while ( TestAndSet (&lock ))
;
// do nothing
//
critical section
lock = FALSE;
//
remainder section
} while (TRUE);
La instrucción Swap
• Definición:
void Swap (boolean *a, boolean *b)
{
boolean temp = *a;
*a = *b;
*b = temp:
}
Solución usando Swap
•
Los procesos comparten la variable booleana lock que
es inicializada FALSE. Cada proceso tiene una variable
local boolena llamada key.
•
Solución:
do {
key = TRUE;
while ( key == TRUE)
Swap (&lock, &key );
// critical section
lock = FALSE;
// remainder section
} while (TRUE);
Discusión
•
Tanto Test and Set como Swap, satisfacen el
requerimiento de exclusión mutua de solución del
problema de la sección crítica, pero no satisfacen el
requerimiento de espera acotada.
•
Tanto, Test and Set como Swap requieren “busy waiting”,
es decir mientras esperan el ingreso, ocupan CPU.
•
Podría ocurrir que los procesos que esperan estén un
tiempo indefinido tratando de ingresar.
•
Veremos una solución aplicada a Test and Set.
Incorporación de espera
acotada a Test and Set
!
do {
! !
waiting[i] = TRUE;
! !
key = TRUE;
! !
while (waiting[i] && key)
! !
!
! !
waiting[i] = FALSE;
! !
!
! !
j = (i + 1) % n;
! !
while ((j != i) && !waiting[j])
! !
!
! !
if (j == i)
! !
!
! !
else
! !
!
waiting[j] = FALSE;
! !
!
// remainder section
key = TestAndSet(&lock);
// critical section
j = (j + 1) % n;
lock = FALSE;
! } while (TRUE);
5 Semáforos
•
Los semáforos son herramientas de
sincronización que no requieren “busy waiting”.
•
•
Un semáforo S contiene una variable entera.
•
Se definen dos operaciones que modifican S:
•
•
wait() (originalmente P())
signal() (originalmente V())
Es más simple que usar Test and Set
... Semáforos
• Variables semáforos sólo son accesibles vía
dos operaciones indivisibles (atómicas):
wait (S) {
while S <= 0
! !
; // no-op
S--;
}
 signal (S) {
S++;
}

Semáforos como herramienta
general de sincronización
• Hay dos tipos de semáforos:
•
Contadores: valor entero sobre un dominio
sin restricción.
•
Binarios: sólo valor entero 1 o 0. Simple de
implementar. También se conoce como mutex.
•
Es posible implementar un semaforo binario
con un semáforo contador.
Exclusión mutua con
semáforo binario
Semaphore mutex;
//
initialized to 1
do {
! wait (mutex);
// Critical Section
signal (mutex);
!!
// remainder section
} while (TRUE);
Implementación de
Semáforos
•
La implementación directa de la definición de
semáforo presenta “busy waiting”. El proceso que
espera gasta ciclos de CPU que podría
aprovechar otro proceso.
•
Estos semáforos se denominan “spinlock” porque
quedan “dando vueltas” mientras esperan. Lo
bueno es que no genera “Context Switch”.
•
Para evitar “busy waiting” es necesario modificar
la definición de semáforo.
Implementación de Semáforos
sin “Busy Waiting”
cada semáforo se asocia una cola de espera.
• Acada
entrada en la cola de espera tiene dos
items de datos:
•
•
•
valor (entero)
puntero al siguiente record de la lista
Se definen dos operaciones:
•
•
block: pone al proceso que la invoca en una cola de
espera apropiada.
wakeup: saca un proceso de la cola de espera y lo
pone en la cola ready.
... Implementación
 Implementation of wait:
!
!
!
!
!
!
!
!
!
!
!
!
wait(semaphore *S) {
!
S->value--;
!
if (S->value < 0) {
!
!
add this process to S->list;
!
!
block();
!
}
}
 Implementation of signal:
!
!
!
!
!
!
!
!
!
!
!
!
signal(semaphore *S) {
!
S->value++;
!
if (S->value <= 0) {
!
!
remove a process P from S->list;
!
!
wakeup(P);
!
}
Ejemplo de uso en
UNIX
• El siguiente ejemplo muestra la forma de
utilizar semáforos en UNIX.
• El programa fuente se llama sem-ex.c
• Para compilar y dejar el ejecutable en
sem-ex:
gcc -o sem-ex sem-ex.c -Wall -Werror -lpthread
• Se trata de entender el código: ¿Qué escribe?
definiciones
main()
thread
salida
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
0:
0:
0:
0:
0:
0:
1:
1:
1:
1:
1:
1:
Waiting to enter critical region...
Now in critical region...
Counter Value: 0
Incrementing Counter...
New Counter Value: 1
Exiting critical region...
Waiting to enter critical region...
Now in critical region...
Counter Value: 1
Incrementing Counter...
New Counter Value: 2
Exiting critical region...
Deadlock y Starvation
•
Abrazo mortal y Inanición son dos problemas que
pueden generar un uso no cuidadoso de
semáforos.
•
Deadlock: dos o más procesos quedan
esperando indefinidamente por un evento que sólo
lo puede generar un proceso que está en espera.
•
Starvation: bloqueo indefinido. Un proceso
nunca es sacado de la cola en la cual está
esperando
Prioridad inversa
•
Esta anomalía se produce por itineración cuando
un proceso de baja prioridad retiene el paso de
uno de mayor prioridad.
•
Ejemplo: procesos A, B, C con prioridades A < B
< C. Supongamos que C necesita el recurso R que
está asignado a A. C debería esperar que A libere
R, pero ahora es itinerado B que interrumpe a C.
•
En síntesis el proceso con menor prioridad,
paraliza a uno de mayor prioridad.
Ejemplos de deadlock
• Sean S y Q dos semáforos inicializados en 1:
P0!
P1
! !
wait (S); !
wait (Q);
!
!
!
!
wait (Q); !
. !
wait (S);
!
!
!
!
.
. !
!
.
!
. !
!
.
!
!
signal
!
!
signal (Q); !
(S); !
signal (Q);
signal (S);
6 Problemas Clásicos
de Sincronización
• Los siguientes problemas se denominan
clásicos y son tratados en (casi) todos los
textos de Sistemas Operativos:
•
•
•
El Buffer de capacidad limitada
Lectores y escritores
Los Filósofos comensales
El Buffer de capacidad
limitada
•
Corresponde al problema del Productor Consumidor.
•
•
•
Un buffer tiene capacidad para N items de datos.
•
El semáforo empty se inicializa en N y cuenta el
número de entradas vacías.
Se inicializa mutex en 1
El semáforo full se inicializa en 0 y cuenta el
número de entradas llenas
Solución gráfica
Buffer
N
P
C
full=0
empty=N
mutex=1
... Buffer: Productor
do
{
//
produce an item in nextp
wait (empty);
wait (mutex);
//
add the item to the
signal (mutex);
signal (full);
} while (TRUE);
buffer
... Buffer: Consumidor
do {
wait (full);
wait (mutex);
//
remove an item from
buffer to nextc
signal (mutex);
signal (empty);
// consume the item in nextc
} while (TRUE);
Lectores y Escritores
• Un conjunto de datos es compartido por un
número de procesos concurrentes que son:
•
•
Lectores: sólo leen, no pueden modificar nada
Escritores: Pueden leer y escribir
• El problema: permitir múltiples lectores al
mismo tiempo. Sólo un escritor tiene acceso
a los datos compartidos al mismo tiempo
datos compartidos
Conjunto de datos
semaforo mutex=1
semaforo wrt=1
semaforo readcount=0
Estructura de proceso
escritor
do {
wait (wrt) ;
//
writing is performed
signal (wrt) ;
} while (TRUE);
Estructura de proceso
escritor
!
! !
! !
do {
wait (mutex) ;
readcount ++ ;
if (readcount == 1)
wait (wrt) ;
signal (mutex)
// reading is performed
wait (mutex) ;
readcount-- ;
if (readcount == 0)
!
signal (wrt) ;
signal (mutex) ;
} while (TRUE);
Filósofos comensales
• 5 filósofos comparten un Bowl de arroz.
• Para comer cada uno utiliza 2 palillos
(chinos).
• Cada filósofo puede pensar o comer. Para
comer necesita 2 palillos
• Datos: Bowl de arroz
• semaforo chopstick[5] inicializado en 1
... Filósofos comensales
Filósofo
do
{
wait ( chopstick[i] );
wait ( chopStick[ (i + 1) %5] );
// eat
signal ( chopstick[i] );
signal (chopstick[ (i + 1) % 5] );
// think
} while (TRUE);
Semáforos:discusión
•
El uso incorrecto puede generar errores. Por
ejemplo:
•
signal(mutex) ........ wait(mutex): deja a
muchos procesos en su sección crítica
•
wait(mutex) ........ wait(mutex): puede
ocurrir deadlock
•
omitir wait(mutex) o signal(mutex)o
ambos: se viola la exclusión mutua o se puede
generar deadlock
7 Monitores
•
Un monitor es un tipo abstracto de datos que
encapsula datos privados y proporciona
métodos públicos
•
Es una abstracción de mayor nivel que los
semáforos y proporcionan un mecanismo
conveniente y efectivo para sincronizar
procesos.
•
Sólo un proceso puede estar activo en el
monitor al mismo tiempo.
Estructura
monitor monitor-name
{
!// shared variable declarations
!procedure P1 (…) { …. }
!!
…
!procedure Pn (…) {……}
Initialization code ( ….) { … }
!!
…
!}
}
Vista esquemática de
un Monitor
Variables de Condición
•
Los monitores proporcionan mecanismos
adicionales de sincronización llamados
variables de condición.
•
•
condition x, y;
Dos operaciones sobre condiciones:
•
•
x.wait(): el proceso se suspende. Se bloquea por la condición
x.signal(): continua uno de los procesos (si hay) que ha invocado
x.wait(). Despierta a un proceso bloqueado sobre la condición.
Monitor con variables
de condición
Solución al problema
de filósofos comensales
monitor DP
{
! enum { THINKING, HUNGRY, EATING} state[5] ;
! condition self[5]; //i tiene hambre, pero debe
// retardarse
! void pickup (int i) {
!
state[i] = HUNGRY;
!
test(i);
!
if (state[i] != EATING)
self[i].wait();
! }
!
void putdown (int i) {
!
state[i] = THINKING;
// test left and right neighbors
!
test((i + 4) % 5);
!
test((i + 1) % 5);
}!
... Solución al problema
void test (int i) {
!
if ((state[(i + 4) % 5] != EATING) &&
!
(state[i] == HUNGRY) &&
!
(state[(i + 1) % 5] != EATING) ) {
!
state[i] = EATING;
! !
self[i].signal();
!
}
! }
!
!
! }
}
initialization_code() {
for (int i = 0; i < 5; i++)
state[i] = THINKING;
... Solución al problema
• Cada filósofo invoca las operaciones
pickup() y putdown() en la siguiente
secuencia:
DiningPhilosophters.pickup(i);
.......
EAT
.........
DiningPhilosophers.putdown(i);
Implementación de
Monitores usando semáforos
• Consideraremos una posible implementación
de monitores utilizando semáforos.
•
Se asigna a cada monitor un semáforo mutex.
Se usa para controlar el número de procesos
en el monitor.
•
•
Antes de entrar: wait(mutex)
Después de abandonar el monitor:
signal(mutex)
... Implementación
•
El semáforo next inicializado en 0 se utiliza
como una cola de espera de procesos que
están en el monitor después de haber sido
liberados de una cola de condición por una
operación signal de monitor.
•
next_count es una variable entera
inicializada en 0 que cuenta los procesos
durmiendo por el semáforo next.
... Implementación:
Variables
!
! !
semaphore mutex; // (initially = 1)
semaphore next; // (initially = 0)
int next-count = 0;
... Implementación
•
Cada procedimiento F se reemplaza por:
wait(mutex);
..........
body of F;
........
if (next_count > 0)
signal(next) // despierta a proceso
else
signal(mutex);
•
next_count sólo se modifica dentro de operaciones de
condición. Si no hay condiciones basta wait(mutex) y signal
(mutex).
•
Se asegura la exclusión mutua.
... Implementación
•
Para cada variable de condición x, tenemos:
! !
•
!
!
!
!
!
!
semaphore x_sem; // (initially
int x_count = 0;
= 0)
La operación x.wait() se puede implementar:
!
!
!
!
!
!
x_count++;
if (next_count > 0)
! signal(next);
else
! signal(mutex);
wait(x_sem);
x_count--;
... Implementación
•
!
!
!
!
!
La operación x.signal() se puede implementar:
!
!
!
!
!
if (x_count > 0) {
! next_count++;
! signal(x_sem);
! wait(next);
! next_count--;
}
Ejemplo: Monitor para
asignar un recurso simple
monitor ResourceAllocator
{
! boolean busy;
! condition x;
! void acquire(int time) {
! !
if (busy)
! !
! x.wait(time);
! !
busy = TRUE;
! }
! void release() {
! !
busy = FALSE;
! !
x.signal();
! }
initialization code() {
!
busy = FALSE;
! }
}! !
!
 Cada proceso especifica el
máximo de tiempo que planea
ocupar el recurso..
 El monitor asigna el recurso al
proceso que requiere el menor
tiempo de asignación.
 Un proceso que necesita
acceso:
R.acquire(t);
.......
access_the_resource;
........
R.release();
8 Sincronización en
Linux
•
Antes del kernel versión 2.6, Linux desabilitaba
las interrupciones para implementar secciones
críticas cortas.
•
Las versiones 2.6 y posteriores son totalmente
interrumplibles (preemtive).
•
Linux proporciona dos mecanismos:
•
•
semáforos
Spin locks (busy waiting)
9 Sincronización de
Pthreads
•
•
•
Las API de Pthreads son independientes de SO
Estas API proporcionan:
•
•
Mutex locks
Variables de condición
Extensiones no portables incluyen:
•
•
read-write locks
Spin locks
Sistemas Operativos
6 Sincronización de Procesos
Prof. Javier Cañas R.
Descargar