Sincronizacin Bajo Nivel Cecilia Hernndez 2007 1 Mltiples
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 vi h 1 h 2 l nueva previa suma g++ h 3 Resultado: No se puede predecir, normalmente difícil de reproducir
Procesos/hebras aislados/no aislados ¢ Procesos/hebras aisladas o independientes: Procesos/hebras no comparten datos entre ellos l 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 l 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 l 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 l 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 global int suma = 0; void *uno(void *p) { int *pi = (int *)p; for (int i = 0; i < *pi; i++) { suma++; } } hebra 1 tiempo hebra 2 lw $t 0, offset($s 0) addi $t 0, 1 sw $t 0, offset($s 0)
Qué hacer? ¢ Nada: Puede estar bien. Si suma pierde actualizaciones no importa. l ¢ No compartir: duplicar estado l l ¢ En la mayoría de las aplicaciones si importa en algún contexto Tratar de maximizar este criterio No siempre posible Hay una solución general? Si l 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 l Si dos hebras ejecutan la misma unidad atómica al mismo tiemp, una hebra ejecutará la secuencia completa antes que la otra comience hebra 1 tiempo hebra 2 lw $t 0, offset($s 0) addi $t 0, 1 sw $t 0, offset($s 0)
Requerimientos de Secciones Críticas ¢ ¢ Exclusión Mutua l A lo más una hebra/proceso puede estar en la sección crítica Progreso l 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 l Si una hebra/proceso está esperando entrar a la sección crítica, entonces en algún momento debe entrar (no esperar infinitamente) Eficiencia l 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 l Cuál es sección crítica en ejemplo? HW podría proporcionar instrucción que permitiera incremento atómico l Aplicable a nuestro ejemplo, pero no general l En realidad HW proporciona instrucciones atómicas que permiten construir primitivas atómicas aplicables a cualquier requerimiento Solución General: locks (bloqueos) l Justo antes de entrar a sección crítica hebra obtiene lock y antes de salir lo libera lock Sección crítica unlock
Primitiva de sincronización : Locks ¢ ¢ Lock: variable compartida con 2 operaciones l lock : obtiene lock, si está usado espera. l 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 l 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); } } R e s u l t a d o: S ó l o u n a h e b r a ejecutando s u m a++ a la v e z Acceso mutuamente exclusivo locks referidos como mutex en e s t e c o n t e x t o Ahora 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? l Muy caro l HW provee instrucciones que permiten implementar locks sin interferir con las interrupciones l Instrucciones atómicas proporcionadas por HW • Test and Set • Atomic swap (aswap) en Intel instrucción xchg • http: //www. intel. com/cd/ids/developer/asmona/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 target = 0 1 target = 1 Retorna rv = 1
Implementando locks con TAS (Test and Set) void lock(int &lock) { while(test_and_set(lock)); } void unlock(int &lock){ lock = 0; Recorde nuestro ejemplo } 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); } }
Swap atómico (aswap) void aswap(int &a, int &b){ int temp = a; a = b; b = temp; } Intercambio es atómico 0 1 a=0 B=1 1 0 a=1 b=0
Implementando locks con aswap lock = 0; //global void lock(int &lock){ int key = 1; while(key == 1)aswap(lock, key); } void unlock(int &lock){ Recorde nuestro ejemplo lock = 0; } Qué tienen en común ambas Implementaciones? 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); } }
Múltiples secciones críticas protegida con locks ¢ Múltiples hebras en ejecución pueden tener múltiples secciones críticas l Cada sección crítica debe estar protegida con su propio lock l Se usan locks para garantizar exclusión mutua P 3 P 2 SC 1 P 3 SC 2 P 4 SC 3
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? l l l 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? l l 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 l ¢ Decisión debería considerarse cuando se sabe cuanto tiempo se necesita hasta la liberación de lock l l ¢ Requiere cambio de estado (bloquear y resumir) de hebras lo que requiere a SO manejo de sus estructuras de datos Si se libera pronto Spinlock es mejor Si no mejor bloqueo Algoritmo l l Spin por el largo del costo del bloqueo Si lock no esta disponible, bloqueo Hebra en ejecución tiempo Espera lock spin bloqueo Costo bloqueo Lock liberado por otra hebra Esta hebra obtiene lock
Desempeño ¢ ¢ Suponga que costo de bloqueo es N ciclos l 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 l Alternativa de bloqueo inmediato habría sido al costo N, pero spinning conseguimos M (y M <= N) Pero caso l Si hebra espera por N ciclos spinning y luego se bloquea y resume porque lock fue liberado l costo bloqueo es 2 N (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; l ¢ pthread_mutex_lock(&suma_lock) l ¢ declara mutex Operación lock sobre variable suma_ lock pthread_mutex_unlock(&suma_lock); l Operación unlock sobre variable suma_lock l 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 l 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 W 1 W 0 SC R 1 R 0 Múltiples hebras lectoras pueden accesar recurso R 1 R 0 SC W 0 Sólo una hebra de escritura puede accesar recurso
Requerimientos soportados por locks ¢ ¢ Exclusión Mutua l A lo más una hebra a la vez está en sección crítica Progreso (no deadlock) l Si hay requerimientos concurrentes debe permitir el progreso de uno. l No depende del hebras que están fuera de sección crítica Espera finita (no starvation) l Hebra intentando entrar en sección crítica eventualmente tendrá éxito NOTA l 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? l l l 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 l Elementales, usados para construir otros Semáforos l Básicos, a veces difíciles de manejar correctamente Monitores l De alto nivel, debe ser soportado por lenguaje de programación l Fácil de usarlos l En Java es posible crear monitores con synchronized() Mensajes l Modelo de comunicación y sincronización basado en tx atómica de datos a través de un canal
- Slides: 30