sincronización bajo nivel
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 PresentationTRANSCRIPT
SincronizaciónBajo Nivel
Cecilia Hernández
2007-1
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
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)
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)
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
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
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)
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
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
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
Implementando locks (1)
lock_t L = 1;lock( L ){
while( L == 0);L = 0;
}unlock( L ){
L = 1;}
Funciona?
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?
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
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
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
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
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?
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
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?
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
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
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
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++)
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;
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;
}
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;
}
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
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
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
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