07/10/2018 Parte III: Concurrencia Lenguajes de Programación III Lic. Rodrigo Velázquez Ing. Gustavo Sosa Capítulo 12. POSIX Threads Capítulo 13. Thread Synchronization Capítulo 14. Critical Sections and Semaphores 2 1 07/10/2018 Capítulo 14. Critical Sections and Semaphores Los programas que administran recursos compartidos deben ejecutar partes de código denominadas secciones críticas de manera mutuamente exclusiva. Este capítulo analiza cómo surgen las secciones críticas y cómo proteger su ejecución por medio de semáforos. Después de presentar una descripción general de la abstracción del semáforo. 3 Tratar con las secciones críticas Imagine un sistema informático en el que todos los usuarios compartan una sola impresora y puedan imprimir simultáneamente. ¿Cómo aparecerá la salida? Si las líneas de trabajos de los usuarios estuvieran entremezcladas, el sistema sería inutilizable. Los dispositivos compartidos, como las impresoras, se denominan recursos exclusivos porque deben ser accedidos por un proceso a la vez. Los procesos deben ejecutar el código que accede a estos recursos compartidos de forma mutuamente exclusiva. Una sección crítica es un segmento de código que debe ejecutarse de manera mutuamente exclusiva, es decir, solo un hilo de ejecución puede estar activo en sus límites. Por ejemplo, el código que modifica una variable compartida se considera parte de una sección crítica, si es posible que otros subprocesos de ejecución accedan a la variable compartida durante la modificación. El problema de la sección crítica se refiere al problema de ejecutar el código de la sección crítica de manera segura, justa y simétrica. 4 2 07/10/2018 Semáforos En 1965, E. W. Dijkstra, propuso la abstracción de semáforos para la gestión de alto nivel de exclusión mutua y sincronización. Un semáforo es una variable entera con dos operaciones atómicas, wait y signal. Otros nombres para wait son; P y lock. Otros nombres para signal son up, V, unlock t post. Si S es mayor que cero, wait prueba y disminuye S en una operación atómica. Si S es igual a cero, wait prueba S y bloquea la llamada en una operación atómica. 5 Semáforos Si los subprocesos están bloqueados en el semáforo, S es igual a cero y signal desbloquea uno de estos subprocesos en espera. Si no hay bloqueos en el semáforo, signal aumenta S. En la terminología POSIX: SEM, las operaciones wait y signal se denominan bloqueo de semáforo y desbloqueo de semáforo, respectivamente. Podemos pensar en un semáforo como un valor entero y una lista de procesos que esperan una operación de señal. 6 3 07/10/2018 Semáforos – Ejemplos void wait(semaphore_t *sp) { if (sp->value > 0) sp->value--; else { <Add this process to sp->list> <block> } } void signal(semaphore_t *sp) { if (sp->list != NULL) <remove a process from sp->list and put in ready state> else sp->value++; } 7 Semáforos Las operaciones de wait y signal deben ser atómicas. Una operación atómica es una operación que, una vez iniciada, se completa de una manera lógicamente indivisible (es decir, sin ninguna otra instrucción relacionada intercalada). En este contexto, ser atómico significa que si un proceso llama a wait, ningún otro proceso puede cambiar el semáforo hasta que el semáforo se decremente o el proceso de llamada se bloquee. La operación de signal es atómica de manera similar. Las implementaciones de semáforos utilizan operaciones atómicas del sistema operativo subyacente para garantizar la ejecución correcta. 8 4 07/10/2018 Semáforos – Ejemplos El siguiente pseudocódigo protege una sección crítica si la variable de semáforo S es inicialmente 1 wait(&S); /* entry section or gatekeeper */ <critical section> signal(&S); /* exit section */ <remainder section> 9 POSIX:SEM - Semáforos sin nombre Un semíforo POSIX: SEM es una variable de tipo sem_t con operaciones atómicas asociadas para inicializar, incrementar y disminuir su valor. La extensión de semáforo POSIX: SEM define dos tipos de semáforos, con nombre y sin nombre. Una implementación admite los semáforos POSIX: SEM si define _POSIX_SEMAPHORES en unistd.h. La diferencia entre semáforos sin nombre y con nombre es análoga a la diferencia entre tuberías ordinarias y tuberías con nombre (FIFO). 10 5 07/10/2018 POSIX:SEM - Semáforos nombrados POSIX:SEM los semáforos nombrados pueden sincronizar procesos que no comparten memoria. Los semáforos nombrados tienen un nombre, una ID de usuario, una ID de grupo y permisos, al igual que los archivos. Un nombre de semáforo es una cadena de caracteres que se ajusta a las reglas de construcción para un nombre de ruta. POSIX no requiere que el nombre aparezca en el sistema de archivos, ni especifica las consecuencias de que dos procesos se refieran al mismo nombre a menos que el nombre comience con el carácter de barra diagonal. Si el nombre comienza con una barra (/), entonces dos procesos (o hilos) que abren el semáforo con ese nombre se refieren al mismo semáforo. En consecuencia, siempre use nombres que comiencen con / para POSIX:SEM con semáforos nombrados. Algunos sistemas operativos imponen otras estricciones en los nombres de semáforos. 11 Crear y abrir semáforos nombrados La función sem_open establece la conexión entre un semáforo nombrado y un valor sem_t. El parámetro de nombre es una cadena que identifica el semáforo por nombre. #include <semaphore.h> sem_t *sem_open(const char *name, int oflag, ...); Si tiene éxito, la función sem_open devuelve la dirección del semáforo. Si no tiene éxito, sem_open retorna SEM_FAILED y establece errno. 12 6 07/10/2018 Cierre y desvinculación de semáforos nombrados. POSIX:SEM los semáforos nombrados tienen permanencia más allá de la ejecución de un solo programa. Los programas individuales pueden cerrar los semáforos nombrados con la función sem_close, pero al hacerlo no se elimina el semáforo del sistema. El sem_close toma un solo parámetro, sem, especificando el semáforo que se cerrará. 13 POSIX:SEM Operaciones de semáforo. La función sem_post implementa la señalización de semáforo clásica. Si no se bloquean subprocesos en sem, entonces sem_post incrementa el valor del semáforo. Si al menos un hilo está bloqueado en sem, entonces el valor del semáforo es cero. En este caso, sem_post hace que uno de los subprocesos bloqueados en sem regrese de su función sem_w a it, y el valor del semáforo permanece en cero. La función sem_post es segura para la señal y puede llamarse desde un manejador de señal. 14 7 07/10/2018 POSIX:SEM Operaciones de semáforo. La función sem_wait implementa la operación clásica de espera de semáforo. Si el valor del semáforo es 0, el subproceso de la llamada se bloquea hasta que se desbloquea con una llamada correspondiente a sem_post o hasta que se interrumpe por una señal. La función sem_try w ait es similar a sem_ w ait, excepto que en lugar de bloquear al intentar disminuir un semáforo de valor cero, devuelve –1 y establece errno en EAG AIN . 15 8