Sincronizacion

3,266 views
3,097 views

Published on

sincronizacion

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
3,266
On SlideShare
0
From Embeds
0
Number of Embeds
55
Actions
Shares
0
Downloads
102
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Sincronizacion

  1. 1. Sincronización Bajo Nivel Cecilia Hernández 2007-1
  2. 2. Múltiples proceesos/hebras en un sistema Seguro? <ul><li>No . Errores si un proceso/hebra escribe estado que podría escribir/leer otro proceso/hebra </li></ul><ul><ul><li>Resultado: No se puede predecir, normalmente difícil de reproducir </li></ul></ul>nueva previa vi g++ suma h1 h2 h3
  3. 3. Procesos/hebras aislados/no aislados <ul><li>Procesos/hebras aisladas o independientes: Procesos/hebras no comparten datos entre ellos </li></ul><ul><ul><li>Resultados de ejecución de procesos/hebras no se ven afectados por la planificación </li></ul></ul><ul><ul><ul><li>Determinístico : Mismas entradas -> mismos resultados </li></ul></ul></ul><ul><ul><ul><li>Ejemplos Multiplicación de matrices, Web server respondiendo requerimientos para diversos clientes </li></ul></ul></ul><ul><li>Procesos/hebras no aisladas: Comparten estado </li></ul><ul><ul><li>Resultados pueden verse afectados por planifiación </li></ul></ul><ul><ul><ul><li>No determínistico : Mismas entradas -> resultados distintos </li></ul></ul></ul><ul><ul><ul><li>Muy difícil de depurar (encontrar errores) </li></ul></ul></ul>
  4. 4. Por qué compartir? <ul><li>Costo </li></ul><ul><ul><li>Comprar M, amortizar costo permitiendo compartir a N entidades (N>M) </li></ul></ul><ul><ul><ul><li>Ejemplos: Una impresora muchos archivos, un computador con muchos procesos, una carretera con muchos autos </li></ul></ul></ul><ul><li>Información </li></ul><ul><ul><li>Un proceso puede necesitar de los resultados de otros </li></ul></ul><ul><ul><ul><li>Proporciona velocidad: hebras ejecutándose concurrentemente/paralelamente </li></ul></ul></ul><ul><ul><ul><li>Proporciona modularidad: capacidad de compartir estado permite separar tareas y comunicarlas sólo cuando es necesario </li></ul></ul></ul><ul><ul><ul><li>Compartir información y recursos muy importante en sociedad moderna. Impresoras, Telefono, internet, etc) </li></ul></ul></ul>
  5. 5. Ejemplo condición de carrera <ul><li>//variable global </li></ul><ul><li>int suma = 0; </li></ul><ul><li>void *uno(void *p) { </li></ul><ul><li>int *pi = (int *)p; </li></ul><ul><li>for (int i = 0; i < *pi; i++) { </li></ul><ul><li>suma++; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>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
  6. 6. Qué hacer? <ul><li>Nada: Puede estar bien. Si suma pierde actualizaciones no importa. </li></ul><ul><ul><li>En la mayoría de las aplicaciones si importa en algún contexto </li></ul></ul><ul><li>No compartir: duplicar estado </li></ul><ul><ul><li>Tratar de maximizar este criterio </li></ul></ul><ul><ul><li>No siempre posible </li></ul></ul><ul><li>Hay una solución general? Si </li></ul><ul><ul><li>Origen del problema? … Entrelazado en la ejecución de hebras </li></ul></ul><ul><ul><ul><li>Entonces, solución prevenirlo </li></ul></ul></ul>
  7. 7. Atomicidad: controlando condiciones de carrera <ul><li>Unidad atómica : secuencia de instrucciones garantizada en su ejecución atómica, como si fuera sólo una </li></ul><ul><ul><li>Si dos hebras ejecutan la misma unidad atómica al mismo tiemp, una hebra ejecutará la secuencia completa antes que la otra comience </li></ul></ul>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)
  8. 8. Requerimientos de Secciones Críticas <ul><li>Exclusión Mutua </li></ul><ul><ul><li>A lo más una hebra/proceso puede estar en la sección crítica </li></ul></ul><ul><li>Progreso </li></ul><ul><ul><li>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 </li></ul></ul><ul><li>Espera finita </li></ul><ul><ul><li>Si una hebra/proceso está esperando entrar a la sección crítica, entonces en algún momento debe entrar (no esperar infinitamente) </li></ul></ul><ul><li>Eficiencia </li></ul><ul><ul><li>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 </li></ul></ul>
  9. 9. Soporte de HW para conseguir atomicidad <ul><li>Idea es prevenir que sólo una hebra a la vez ejecute sección crítica </li></ul><ul><ul><li>Cuál es sección crítica en ejemplo? </li></ul></ul><ul><li>HW podría proporcionar instrucción que permitiera incremento atómico </li></ul><ul><ul><li>Aplicable a nuestro ejemplo, pero no general </li></ul></ul><ul><ul><li>En realidad HW proporciona instrucciones atómicas que permiten construir primitivas atómicas aplicables a cualquier requerimiento </li></ul></ul><ul><li>Solución General: locks (bloqueos) </li></ul><ul><ul><li>Justo antes de entrar a sección crítica hebra obtiene lock y antes de salir lo libera </li></ul></ul>Sección crítica lock unlock
  10. 10. Primitiva de sincronización : Locks <ul><li>Lock: variable compartida con 2 operaciones </li></ul><ul><ul><li>lock : obtiene lock, si está usado espera. </li></ul></ul><ul><ul><li>unlock : libera lock, si hay alguien esperando por el puede obtenerlo </li></ul></ul><ul><li>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 </li></ul><ul><ul><li>Nuestro ejemplo usando locks sería </li></ul></ul>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 vez Acceso mutuamente exclusivo locks referidos como mutex en este contexto Ahora sección crítica es atómica
  11. 11. Implementando locks (1) <ul><li>lock_t L = 1; </li></ul><ul><li>lock( L ){ </li></ul><ul><li>while( L == 0); </li></ul><ul><li>L = 0; </li></ul><ul><li>} </li></ul><ul><li>unlock( L ){ </li></ul><ul><li>L = 1; </li></ul><ul><li>} </li></ul><ul><li>Funciona? </li></ul>
  12. 12. Implementando locks (2) <ul><li>Para sistema con un procesador </li></ul><ul><li>lock( L ){ </li></ul><ul><li>desabilitat_int(); </li></ul><ul><li>while( L == 0); </li></ul><ul><li>L = 0; </li></ul><ul><li>habilita_int(); </li></ul><ul><li>} </li></ul><ul><li>unlock( L ){ </li></ul><ul><li>L = 1; </li></ul><ul><li>} </li></ul><ul><li>Funciona? Qué pasa si lock está tomado? </li></ul>
  13. 13. Implementando locks en multiprocesadores <ul><li>Desabilitando interrupciones en todos los procesadores? </li></ul><ul><ul><li>Muy caro </li></ul></ul><ul><ul><li>HW provee instrucciones que permiten implementar locks sin interferir con las interrupciones </li></ul></ul><ul><ul><li>Instrucciones atómicas proporcionadas por HW </li></ul></ul><ul><ul><ul><li>Test and Set </li></ul></ul></ul><ul><ul><ul><li>Atomic swap (aswap) en Intel instrucción xchg </li></ul></ul></ul><ul><ul><ul><ul><li>http://www.intel.com/cd/ids/developer/asmo-na/eng/dc/threading/333935.htm (implementando locks escalables en Multicores) </li></ul></ul></ul></ul><ul><ul><ul><li>Ambas se utilizan para implementar locks </li></ul></ul></ul>
  14. 14. Instrucción Test and Set <ul><li>int test_and_set(int &target){ </li></ul><ul><li>int rv = target; </li></ul><ul><li>target = 1; </li></ul><ul><li>return rv; </li></ul><ul><li>} </li></ul><ul><li>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 </li></ul>0 1 target = 0 target = 1 Retorna rv = 0 1 1 target = 1 target = 1 Retorna rv = 1
  15. 15. Implementando locks con TAS (Test and Set) <ul><li>void lock (int &lock) { </li></ul><ul><li>while(test_and_set(lock)); </li></ul><ul><li>} </li></ul><ul><li>void unlock (int &lock){ </li></ul><ul><li>lock = 0; </li></ul><ul><li>} </li></ul>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
  16. 16. Swap atómico (aswap) <ul><li>void aswap(int &a, int &b){ </li></ul><ul><li>int temp = a; </li></ul><ul><li>a = b; </li></ul><ul><li>b = temp; </li></ul><ul><li>} </li></ul><ul><li>Intercambio es atómico </li></ul>0 1 a = 0 B = 1 a = 1 b = 0 0 1
  17. 17. Implementando locks con aswap <ul><li>lock = 0; //global </li></ul><ul><li>void lock (int &lock){ </li></ul><ul><li>int key = 1; </li></ul><ul><li>while(key == 1)aswap(lock, key); </li></ul><ul><li>} </li></ul><ul><li>void unlock (int &lock){ </li></ul><ul><li>lock = 0; </li></ul><ul><li>} </li></ul>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?
  18. 18. Múltiples secciones críticas protegida con locks <ul><li>Múltiples hebras en ejecución pueden tener múltiples secciones críticas </li></ul><ul><ul><li>Cada sección crítica debe estar protegida con su propio lock </li></ul></ul><ul><ul><li>Se usan locks para garantizar exclusión mutua </li></ul></ul>SC1 SC2 SC3 P2 P3 P1 P3 P4
  19. 19. Spinlocks <ul><li>Hebras spin (se dan vuelta en loop) hasta que obtienen lock </li></ul><ul><li>Implementaciones previas usando TAS y aswap son de este tipo </li></ul><ul><li>Problemas con spinning? </li></ul><ul><ul><li>Utilizan CPU para verificar estado sin poder progresar en ejecución </li></ul></ul><ul><ul><li>Por qué podría ser bueno? </li></ul></ul><ul><ul><li>Por qué podría se malo? </li></ul></ul><ul><ul><ul><li>Alguna alternativa? </li></ul></ul></ul>
  20. 20. Spinlocks en un procesador <ul><li>Por cuánto tiempo la hebra que espera lock usa CPU? </li></ul><ul><ul><li>Hasta que consigue lock, para ello cada vez que esta en CPU consume su quantum esperando </li></ul></ul><ul><ul><li>Normalmente en este sistema en lugar se spin se bloquea </li></ul></ul><ul><ul><ul><li>Locks implementan colas de espera cuando lock es liberado se entrega lock a la primera hebra en la cola de espera de lock </li></ul></ul></ul>
  21. 21. Spin o bloqueo en multiprocesador <ul><li>Hebras pueden ejecutarse en multiples CPU </li></ul><ul><li>Bloqueo no es gratis </li></ul><ul><ul><li>Requiere cambio de estado (bloquear y resumir) de hebras lo que requiere a SO manejo de sus estructuras de datos </li></ul></ul><ul><li>Decisión debería considerarse cuando se sabe cuanto tiempo se necesita hasta la liberación de lock </li></ul><ul><ul><li>Si se libera pronto Spinlock es mejor </li></ul></ul><ul><ul><li>Si no mejor bloqueo </li></ul></ul><ul><li>Algoritmo </li></ul><ul><ul><li>Spin por el largo del costo del bloqueo </li></ul></ul><ul><ul><li>Si lock no esta disponible, bloqueo </li></ul></ul>tiempo Costo bloqueo spin bloqueo Espera lock Lock liberado por otra hebra Esta hebra obtiene lock Hebra en ejecución
  22. 22. Desempeño <ul><li>Suponga que costo de bloqueo es N ciclos </li></ul><ul><ul><li>Bloquear y resumir hebra </li></ul></ul><ul><li>Si hebra obtiene lock después de M ciclos de espera ocupada (spinning) (M <= N) entonces M es el costo óptimo </li></ul><ul><ul><li>Alternativa de bloqueo inmediato habría sido al costo N, pero spinning conseguimos M (y M <= N) </li></ul></ul><ul><li>Pero caso </li></ul><ul><ul><li>Si hebra espera por N ciclos spinning y luego se bloquea y resume porque lock fue liberado </li></ul></ul><ul><ul><li>costo bloqueo es 2N (un N para spinning y N para bloqueo y resumen de hebra) </li></ul></ul><ul><li>Desempeño siempre dentro de factor de 2 del óptimo </li></ul>
  23. 23. Ejemplo exclusión mutua usando locks mutex pthreads <ul><li>void *uno(void *p) { </li></ul><ul><li>int *pi = (int *)p; </li></ul><ul><li>for (int i = 0; i < *pi; i++) { </li></ul><ul><li>suma++; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>Condición de carrera </li></ul><ul><li>suma++ (3 instrucciones de lenguaje de máquina) </li></ul><ul><li>Sección crítica </li></ul><ul><li>suma++ </li></ul><ul><li>Sincronización </li></ul><ul><li> Usando locks (hacer atómica las tres instrucciones que componen suma++) </li></ul>
  24. 24. Mutex pthreads <ul><li>pthread_mutex_t suma_lock; </li></ul><ul><ul><li>declara mutex </li></ul></ul><ul><li>pthread_mutex_lock(&suma_lock) </li></ul><ul><ul><li>Operación lock sobre variable suma_ lock </li></ul></ul><ul><li>pthread_mutex_unlock(&suma_lock); </li></ul><ul><ul><li>Operación unlock sobre variable suma_lock </li></ul></ul><ul><ul><li>suma_lock = PTHREAD_MUTEX_INITIALIZER; </li></ul></ul>
  25. 25. Ejemplo (1) <ul><li>pthread_mutex_t suma_lock; </li></ul><ul><li>void *uno(void *p) { </li></ul><ul><li>int *pi = (int *)p; </li></ul><ul><li>pthread_mutex_lock(&suma_lock); </li></ul><ul><li>for (int i = 0; i < *pi; i++) { </li></ul><ul><li>suma++; </li></ul><ul><li>} </li></ul><ul><li>pthread_mutex_unlock(&suma_lock); </li></ul><ul><li>} </li></ul><ul><li>main(){ </li></ul><ul><li>suma_lock = PTHREAD_MUTEX_INITIALIZER; </li></ul><ul><li>} </li></ul>
  26. 26. Ejemplo (2) <ul><li>pthread_mutex_t suma_lock; </li></ul><ul><li>void *uno(void *p) { </li></ul><ul><li>int *pi = (int *)p; </li></ul><ul><li>for (int i = 0; i < *pi; i++) { </li></ul><ul><li>pthread_mutex_lock(&suma_lock); </li></ul><ul><li>suma++; </li></ul><ul><li>pthread_mutex_unlock(&suma_lock); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>Main(){ </li></ul><ul><li>suma_lock = PTHREAD_MUTEX_INITIALIZER; </li></ul><ul><li>} </li></ul>
  27. 27. Locks read/write <ul><li>Utilizados por aplicaciones que permiten mayor concurrencia </li></ul><ul><ul><li>El requerimiento de exclusión mutua es para evitar problemas de lectura/escritura </li></ul></ul><ul><ul><ul><li>Hebras que leen y hebras que escriben no pueden inteferir en sus operaciones </li></ul></ul></ul><ul><ul><ul><li>Múltiples hebras pueden estar leyendo simultaneamente </li></ul></ul></ul>SC W0 W1 R0 R1 Múltiples hebras lectoras pueden accesar recurso SC R0 R1 W0 Sólo una hebra de escritura puede accesar recurso
  28. 28. Requerimientos soportados por locks <ul><li>Exclusión Mutua </li></ul><ul><ul><li>A lo más una hebra a la vez está en sección crítica </li></ul></ul><ul><li>Progreso (no deadlock) </li></ul><ul><ul><li>Si hay requerimientos concurrentes debe permitir el progreso de uno. </li></ul></ul><ul><ul><li>No depende del hebras que están fuera de sección crítica </li></ul></ul><ul><li>Espera finita (no starvation) </li></ul><ul><ul><li>Hebra intentando entrar en sección crítica eventualmente tendrá éxito </li></ul></ul><ul><li>NOTA </li></ul><ul><ul><li>Estos requerimientos no necesariamente se cumplen si no se utilizan adecuadamente </li></ul></ul><ul><ul><ul><li>Requerimientos pueden verse afectados si programación de aplicación con hebras no incluyen pares lock/unlock adecuadamente </li></ul></ul></ul>
  29. 29. Resumen <ul><li>Múltiples hebras + compartiendo estado/recursos = condiciones de carrera </li></ul><ul><li>Como solucionarlo? </li></ul><ul><ul><li>Estado privado no necesita sincronización </li></ul></ul><ul><ul><li>Problemas de condición de carrera difíciles de reproducir y descubrir </li></ul></ul><ul><ul><li>Locks es una alternativa de bajo nivel para incorporar sincronización y evitar condiciones de carrera </li></ul></ul><ul><ul><ul><li>Spin locks, bloqueo </li></ul></ul></ul><ul><ul><ul><li>Se requiere hacerlo bien para respetar requerimientos de transparencia anterior </li></ul></ul></ul>
  30. 30. Mecanismos de Sincronización para Secciones Críticas <ul><li>Locks </li></ul><ul><ul><li>Elementales, usados para construir otros </li></ul></ul><ul><li>Semáforos </li></ul><ul><ul><li>Básicos, a veces difíciles de manejar correctamente </li></ul></ul><ul><li>Monitores </li></ul><ul><ul><li>De alto nivel, debe ser soportado por lenguaje de programación </li></ul></ul><ul><ul><li>Fácil de usarlos </li></ul></ul><ul><ul><li>En Java es posible crear monitores con synchronized() </li></ul></ul><ul><li>Mensajes </li></ul><ul><ul><li>Modelo de comunicación y sincronización basado en tx atómica de datos a través de un canal </li></ul></ul>

×