Sincronización Bajo Nivel

30
Sincronización Bajo Nivel Cecilia Hernández 2007-1

description

Sincronización Bajo Nivel. Cecilia Hernández 2007-1. Múltiples proceesos/hebras en un sistema Seguro?. No . Errores si un proceso/hebra escribe estado que podría escribir/leer otro proceso/hebra Resultado: No se puede predecir, normalmente difícil de reproducir. nueva. vi. g++. previa. - PowerPoint PPT Presentation

Transcript of Sincronización Bajo Nivel

Page 1: Sincronización Bajo Nivel

SincronizaciónBajo Nivel

Cecilia Hernández

2007-1

Page 2: Sincronización Bajo Nivel

Múltiples proceesos/hebras en un sistemaSeguro?

No. Errores si un proceso/hebra escribe estado que podría escribir/leer otro proceso/hebra

Resultado: No se puede predecir, normalmente difícil de reproducir

nueva

previavi g++

sumah1

h2h3

Page 3: Sincronización Bajo Nivel

Procesos/hebras aislados/no aislados Procesos/hebras aisladas o independientes:

Procesos/hebras no comparten datos entre ellos Resultados de ejecución de procesos/hebras no

se ven afectados por la planificación• Determinístico : Mismas entradas -> mismos

resultados• Ejemplos Multiplicación de matrices, Web server

respondiendo requerimientos para diversos clientes Procesos/hebras no aisladas: Comparten

estado Resultados pueden verse afectados por

planifiación• No determínistico : Mismas entradas -> resultados

distintos• Muy difícil de depurar (encontrar errores)

Page 4: Sincronización Bajo Nivel

Por qué compartir?

Costo Comprar M, amortizar costo permitiendo compartir a N

entidades (N>M)• Ejemplos: Una impresora muchos archivos, un computador

con muchos procesos, una carretera con muchos autos Información

Un proceso puede necesitar de los resultados de otros• Proporciona velocidad: hebras ejecutándose

concurrentemente/paralelamente • Proporciona modularidad: capacidad de compartir estado

permite separar tareas y comunicarlas sólo cuando es necesario

• Compartir información y recursos muy importante en sociedad moderna. Impresoras, Telefono, internet, etc)

Page 5: Sincronización Bajo Nivel

Ejemplo condición de carrera//variable globalint suma = 0;

void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { suma++; }}

tiempo lw $t0, offset($s0)

lw $t0, offset($s0)

addi $t0, $t0, 1

sw $t0, offset($s0)

addi $t0, $t0, 1

sw $t0, offset($s0)

hebra1 hebra2

Page 6: Sincronización Bajo Nivel

Qué hacer?

Nada: Puede estar bien. Si suma pierde actualizaciones no importa. En la mayoría de las aplicaciones si importa

en algún contexto No compartir: duplicar estado

Tratar de maximizar este criterio No siempre posible

Hay una solución general? Si Origen del problema? … Entrelazado en la

ejecución de hebras• Entonces, solución prevenirlo

Page 7: Sincronización Bajo Nivel

Atomicidad: controlando condiciones

de carrera Unidad atómica : secuencia de instrucciones

garantizada en su ejecución atómica, como si fuera sólo una Si dos hebras ejecutan la misma unidad atómica al

mismo tiemp, una hebra ejecutará la secuencia completa antes que la otra comience

tiempo

hebra1 hebra2

lw $t0, offset($s0)

addi $t0, $t0, 1

sw $t0, offset($s0)

lw $t0, offset($s0)

addi $t0, $t0, 1

sw $t0, offset($s0)

Page 8: Sincronización Bajo Nivel

Requerimientos de Secciones Críticas

Exclusión Mutua A lo más una hebra/proceso puede estar en la sección crítica

Progreso Si una hebra/proceso no está en sección crítica, entonces

hebra/proceso no puede impedir que otra hebra/proceso ingrese a sección crítica

Espera finita Si una hebra/proceso está esperando entrar a la sección

crítica, entonces en algún momento debe entrar (no esperar infinitamente)

Eficiencia El overhead de entrar/salir de la sección crítica debe ser

pequeño en comparación con el tiempo que toma la sección crítica

Page 9: Sincronización Bajo Nivel

Soporte de HW para conseguir atomicidad

Idea es prevenir que sólo una hebra a la vez ejecute sección crítica Cuál es sección crítica en ejemplo?

HW podría proporcionar instrucción que permitiera incremento atómico Aplicable a nuestro ejemplo, pero no general En realidad HW proporciona instrucciones atómicas

que permiten construir primitivas atómicas aplicables a cualquier requerimiento

Solución General: locks (bloqueos) Justo antes de entrar a sección crítica hebra obtiene

lock y antes de salir lo libera

Sección crítica

lock

unlock

Page 10: Sincronización Bajo Nivel

Primitiva de sincronización : Locks Lock: variable compartida con 2 operaciones

lock : obtiene lock, si está usado espera. unlock : libera lock, si hay alguien esperando por el

puede obtenerlo Cómo se usa? Al identificar sección crítica en código

se utiliza lock y unlock al principio y fin de sección Nuestro ejemplo usando locks sería

lock_t suma_lock=INIT;void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { lock(suma_lock); suma++; unlock(suma_lock); }}

Resultado: Sólo una hebra ejecutando suma++ a la vezAcceso mutuamente exclusivolocks referidos como mutex eneste contextoAhora sección crítica es atómica

Page 11: Sincronización Bajo Nivel

Implementando locks (1)

lock_t L = 1;lock( L ){

while( L == 0);L = 0;

}unlock( L ){

L = 1;}

Funciona?

Page 12: Sincronización Bajo Nivel

Implementando locks (2) Para sistema con un procesador

lock( L ){desabilitat_int();while( L == 0);L = 0;habilita_int();

}

unlock( L ){L = 1;

}

Funciona? Qué pasa si lock está tomado?

Page 13: Sincronización Bajo Nivel

Implementando locks en multiprocesadores

Desabilitando interrupciones en todos los procesadores? Muy caro HW provee instrucciones que permiten implementar

locks sin interferir con las interrupciones Instrucciones atómicas proporcionadas por HW

• Test and Set• Atomic swap (aswap) en Intel instrucción xchg

• http://www.intel.com/cd/ids/developer/asmo-na/eng/dc/threading/333935.htm (implementando locks escalables en Multicores)

• Ambas se utilizan para implementar locks

Page 14: Sincronización Bajo Nivel

Instrucción Test and Set

int test_and_set(int &target){int rv = target;target = 1;return rv;

} Atómicamente verifica si la celda de memoria es

cero, si es así la setea en 1. Si es 1 no hace nada. Retorna el valor antiguo

0 1

target = 0 target = 1Retorna rv = 0

1 1

target = 1 target = 1Retorna rv = 1

Page 15: Sincronización Bajo Nivel

Implementando locks con TAS (Test and Set)void lock(int &lock) {

while(test_and_set(lock));}void unlock(int &lock){

lock = 0;}

void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { lock(suma_lock); suma++; unlock(suma_lock); }}

lock_t suma_lock=INIT;

Recorde nuestro ejemplo

Page 16: Sincronización Bajo Nivel

Swap atómico (aswap)

void aswap(int &a, int &b){int temp = a;a = b;b = temp;

}Intercambio es atómico

0 1

a = 0B = 1

a = 1b = 0

01

Page 17: Sincronización Bajo Nivel

Implementando locks con aswap

lock = 0; //globalvoid lock(int &lock){

int key = 1;while(key == 1)aswap(lock, key);

}void unlock(int &lock){

lock = 0;}

lock_t suma_lock=INIT;

Recorde nuestro ejemplo

void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { lock(suma_lock); suma++; unlock(suma_lock); }}

Qué tienen en común ambas Implementaciones?

Page 18: Sincronización Bajo Nivel

Múltiples secciones críticasprotegida con locks

Múltiples hebras en ejecución pueden tener múltiples secciones críticas Cada sección crítica debe estar protegida con su

propio lock Se usan locks para garantizar exclusión mutua

SC1

SC2

SC3

P2P3 P1

P3 P4

Page 19: Sincronización Bajo Nivel

Spinlocks

Hebras spin (se dan vuelta en loop) hasta que obtienen lock

Implementaciones previas usando TAS y aswap son de este tipo

Problemas con spinning? Utilizan CPU para verificar estado sin poder

progresar en ejecución Por qué podría ser bueno? Por qué podría se malo?

• Alguna alternativa?

Page 20: Sincronización Bajo Nivel

Spinlocks en un procesador

Por cuánto tiempo la hebra que espera lock usa CPU? Hasta que consigue lock, para ello cada vez

que esta en CPU consume su quantum esperando

Normalmente en este sistema en lugar se spin se bloquea

• Locks implementan colas de espera cuando lock es liberado se entrega lock a la primera hebra en la cola de espera de lock

Page 21: Sincronización Bajo Nivel

Spin o bloqueo en multiprocesador

Hebras pueden ejecutarse en multiples CPU Bloqueo no es gratis

Requiere cambio de estado (bloquear y resumir) de hebras lo que requiere a SO manejo de sus estructuras de datos

Decisión debería considerarse cuando se sabe cuanto tiempo se necesita hasta la liberación de lock

Si se libera pronto Spinlock es mejor Si no mejor bloqueo

Algoritmo Spin por el largo del costo del bloqueo Si lock no esta disponible, bloqueo

tiempo Costo bloqueospin

bloqueo

Espera lock

Lock liberado por otra hebraEsta hebra obtiene lock

Hebra en ejecución

Page 22: Sincronización Bajo Nivel

Desempeño

Suponga que costo de bloqueo es N ciclos Bloquear y resumir hebra

Si hebra obtiene lock después de M ciclos de espera ocupada (spinning) (M <= N) entonces M es el costo óptimo Alternativa de bloqueo inmediato habría sido al costo

N, pero spinning conseguimos M (y M <= N) Pero caso

Si hebra espera por N ciclos spinning y luego se bloquea y resume porque lock fue liberado

costo bloqueo es 2N (un N para spinning y N para bloqueo y resumen de hebra)

Desempeño siempre dentro de factor de 2 del óptimo

Page 23: Sincronización Bajo Nivel

Ejemplo exclusión mutua usando locks mutex pthreads

void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { suma++; }}Condición de carrera

suma++ (3 instrucciones de lenguaje de máquina)Sección crítica

suma++Sincronización

Usando locks (hacer atómica las tres instrucciones que componen suma++)

Page 24: Sincronización Bajo Nivel

Mutex pthreads

pthread_mutex_t suma_lock; declara mutex

pthread_mutex_lock(&suma_lock) Operación lock sobre variable suma_ lock

pthread_mutex_unlock(&suma_lock); Operación unlock sobre variable suma_lock suma_lock = PTHREAD_MUTEX_INITIALIZER;

Page 25: Sincronización Bajo Nivel

Ejemplo (1)

pthread_mutex_t suma_lock;void *uno(void *p) { int *pi = (int *)p; pthread_mutex_lock(&suma_lock); for (int i = 0; i < *pi; i++) {

suma++; } pthread_mutex_unlock(&suma_lock);}

main(){suma_lock = PTHREAD_MUTEX_INITIALIZER;

}

Page 26: Sincronización Bajo Nivel

Ejemplo (2)

pthread_mutex_t suma_lock;void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { pthread_mutex_lock(&suma_lock); suma++; pthread_mutex_unlock(&suma_lock); }}

Main(){suma_lock = PTHREAD_MUTEX_INITIALIZER;

}

Page 27: Sincronización Bajo Nivel

Locks read/write

Utilizados por aplicaciones que permiten mayor concurrencia El requerimiento de exclusión mutua es para

evitar problemas de lectura/escritura• Hebras que leen y hebras que escriben no

pueden inteferir en sus operaciones• Múltiples hebras pueden estar leyendo

simultaneamente

SCW0W1 R0R1

Múltiples hebras lectoras puedenaccesar recurso

SCR0R1 W0

Sólo una hebra de escriturapuede accesar recurso

Page 28: Sincronización Bajo Nivel

Requerimientos soportados por locks

Exclusión Mutua A lo más una hebra a la vez está en sección crítica

Progreso (no deadlock) Si hay requerimientos concurrentes debe permitir el

progreso de uno. No depende del hebras que están fuera de sección

crítica Espera finita (no starvation)

Hebra intentando entrar en sección crítica eventualmente tendrá éxito

NOTA Estos requerimientos no necesariamente se cumplen si

no se utilizan adecuadamente• Requerimientos pueden verse afectados si programación

de aplicación con hebras no incluyen pares lock/unlock adecuadamente

Page 29: Sincronización Bajo Nivel

Resumen

Múltiples hebras + compartiendo estado/recursos = condiciones de carrera

Como solucionarlo? Estado privado no necesita sincronización Problemas de condición de carrera difíciles de

reproducir y descubrir Locks es una alternativa de bajo nivel para

incorporar sincronización y evitar condiciones de carrera

• Spin locks, bloqueo• Se requiere hacerlo bien para respetar requerimientos

de transparencia anterior

Page 30: Sincronización Bajo Nivel

Mecanismos de Sincronización para Secciones Críticas

Locks Elementales, usados para construir otros

Semáforos Básicos, a veces difíciles de manejar correctamente

Monitores De alto nivel, debe ser soportado por lenguaje de

programación Fácil de usarlos En Java es posible crear monitores con synchronized()

Mensajes Modelo de comunicación y sincronización basado en tx

atómica de datos a través de un canal