Uno de los contenidos fundamentales de la programación paralela es la sincronización con semáforos. Este concepto es el que os vamos a explicar en este artículo.
Por si quedaba alguna duda, no son semáforos de tráfico, es un concepto usado en informática para indicar una estructura con unas características específicas.
Un semáforo es una estructura u objeto que nos va a permitir la sincronización multiproceso o multihilo de nuestro programa. Básicamente se basa en un contador, una función de incrementar el contador y otra de decrementar el contador. El control de acceso se realiza mediante el valor de ese contador, si el contador es negativo el hilo o proceso se suspenderá hasta que ese contador deje de ser negativo, momento en el cual el hilo o proceso empezará a ejecutarse automáticamente.
La implementación de los semáforos varía entre plataformas, por ejemplo en Linux están implementadas a nivel del sistema operativo dentro de los IPCs.
Si estáis más interesados en cómo funciona un semáforo por dentro podéis leer los problemas: problema del barbero y el problema de los filósofos.
Un semáforo al final funciona de manera similar al letrero del parking que se puede ver en la imagen superior. Mientras que haya plazas libres, deja entrar a los procesos o hilos y cuando hay 0 plazas hay que esperar a que otros salgan para poder entrar.
La función wait comprueba que el valor del semáforo, si es negativo o cero el proceso se suspende sin consumir recursos hasta que el valor deje de ser negativo. Si el valor es positivo el proceso o hilo, sigue ejecutándose y se decrementa en una unidad el valor del semáforo. Siguiendo con la analogía del parking, hacer un wait es coger el ticket del aparcamiento cuando hay sitio.
La función signal incremental el valor del semáforo, este comportamiento debe realizarse cuando se ha salido de la zona de exclusión mutua. En nuestro caso sería similar a entregar el ticket y salir del parking.
Este código muestra como funciona la exclusión mutua con semáforos:
/*
* Programa creado por Jorge Duran para Somos Binarios
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/sem.h>
/****************************************************************
Funciones auxiliares para inicializar, hacer wait y hacer Signal
Funcionan con arrays de semaforos, si solo hay uno ese parametro es 0
*******************************************************************/void error(char* errorInfo) {
fprintf(stderr,"%s",errorInfo);
exit(1);
}
void doSignal(int semid, int numSem) {
struct sembuf sops; //Signal
sops.sem_num = numSem;
sops.sem_op = 1;
sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror(NULL);
error("Error al hacer Signal");
}
}
void doWait(int semid, int numSem) {
struct sembuf sops;
sops.sem_num = numSem; /* Sobre el primero, ... */ sops.sem_op = -1; /* ... un wait (resto 1) */ sops.sem_flg = 0;
if (semop(semid, &sops, 1) == -1) {
perror(NULL);
error("Error al hacer el Wait");
}
}
void initSem(int semid, int numSem, int valor) { //iniciar un semaforo
if (semctl(semid, numSem, SETVAL, valor) < 0) {
perror(NULL);
error("Error iniciando semaforo");
}
}
int main() {
puts("Sincronizacion con Semaforos ");
int semaforo;
//Manera de usar semget http://pubs.opengroup.org/onlinepubs/7908799/xsh/semget.html
//Creamos un semaforo y damos permisos para compartirlo
if((semaforo=semget(IPC_PRIVATE,1,IPC_CREAT | 0700))<0) {
perror(NULL);
error("Semaforo: semget");
}
initSem(semaforo,0,1);
puts("Hay una plaza libre");
switch (fork())
{
case -1:
error("Error en el fork");
case 0: /* Hijo */ doWait(semaforo,0);
puts("Entro el hijo, el padre espera");
sleep(5);
puts("El hijo sale");
doSignal(semaforo,0);
exit(0);
default: /* Padre */ doWait(semaforo,0);
puts("Entro el padre, el hijo espera");
sleep(5);
puts("El padre sale");
doSignal(semaforo,0);
}
sleep(20);
//Liberacion del semaforo
if ((semctl(semaforo, 0, IPC_RMID)) == -1) {
perror(NULL);
error("Semaforo borrando");
}
return 0;
}
La ejecución es similar a esta (según si entra antes el padre o el hijo, porque eso nunca lo podremos saber hasta ejecutarlo):
Espero que os gusten esta serie de artículos y os animéis a usar estos conocimientos en algunos de vuestros proyectos.
Hoy queremos hablaros de Docker un proyecto que cada día es más usado, porque permite…
Cada vez estamos más acostumbrados a usar código para generar la infraestructura (IaC), documentar nuestro…
Uno de los problemas que se presentan con una mayor frecuencia hoy en día, es…
Uno de los problemas que solemos tener los programadores, es que nos gusta estar a…
Docker es una de las herramientas más usadas por los desarrolladores, sin embargo, usarlo en…
Como seguramente sabrás el uso de JavaScript ha crecido exponencialmente en los últimos tiempos, sin…