La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

Comunicación y sincronización entre procesos

Presentaciones similares


Presentación del tema: "Comunicación y sincronización entre procesos"— Transcripción de la presentación:

1 Comunicación y sincronización entre procesos
María de los Santos Pérez Hernández

2 Índice Procesos concurrentes. El problema de la sección crítica.
Problemas clásicos de comunicación y sincronización. Mecanismos de comunicación y sincronización. Interbloqueos.

3 Referencias bibliográficas
“Sistemas Operativos: una visión aplicada” Jesús Carretero et al. McGraw Hill, 2001 “Sistemas Operativos” Willian Stalling Willian Stalling Prentice Hall, 1997 “Operating System Concepts” A. Silberschatz, P. Galvin Addison-Wesley, 1998

4 Procesos concurrentes (I)
Modelos Multiprogramación en un único procesador Multiprocesador Multicomputador (proceso distribuido) Razones Compartir recursos físicos Compartir recursos lógicos Acelerar los cálculos Modularidad Comodidad

5 Procesos concurrentes (II)
Tipos de procesos Independientes Cooperantes Interacción entre procesos Compiten por recursos Comparten recursos

6 Problema de la interacción entre procesos (procesos ligeros)
Calcula la suma de los N primeros números utilizando procesos ligeros. int suma_total = 0; void suma_parcial(int ni, int nf) { int j = 0; int suma_parcial = 0; for (j = ni; j <= nf; j++) suma_parcial = suma_parcial +j; suma_total = suma_total + suma_parcial; pthread_exit(0); } Si varios procesos ejecutan concurrentemente este código se puede obtener un resultado incorrecto. Solución: secciones críticas

7 Ejemplo con sección crítica (procesos ligeros)
void suma_parcial(int ni, int nf) { int j = 0; int suma_parcial = 0; for (j = ni; j <= nf; j++) suma_parcial = suma_parcial + j; Entrada en la sección crítica suma_total = suma_total+ suma_parcial; Salida de la sección crítica pthread_exit(0); } Requisitos para ofrecer secciones críticas: Exclusión mutua Progreso Espera limitada

8 Problema de la interacción entre procesos
void ingresar(char *cuenta, int cantidad) { int saldo, fd; fd = open(cuenta, O_RDWR); read(fd, &saldo,sizeof(int)); saldo = saldo + cantidad; lseek(fd, 0, SEEK_SET); write(fd, &saldo,sizeof(int)); close(fd); return; } Si dos procesos ejecutan concurrentemente este código se puede perder algún ingreso. Solución: secciones críticas

9 Ejemplo con sección crítica
void ingresar(char *cuenta, int cantidad) { int saldo, fd; fd = open(cuenta, O_RDWR); Entrada en la sección crítica read(fd, &saldo, sizeof(int)); saldo = saldo + cantidad; lseek(fd, 0, SEEK_SET); write(fd, &saldo, sizeof(int)); Salida de la sección crítica close(fd); return; } Requisitos para ofrecer secciones críticas: Exclusión mutua Progreso Espera limitada

10 Mecanismos de comunicación
Ficheros Pipes FIFOS Variables en memoria compartida Paso de mensajes Sockets

11 Mecanismos de Sincronización
Construcciones de los lenguajes concurrentes (procesos ligeros) Servicios del sistema operativo: Señales (asincronismo) Pipes FIFOS Semáforos Mutex y variables condicionales Paso de mensajes Las operaciones de sincronización deben ser atómicas

12 Características de los mecanismos de comunicación
Identificación Mecanismos de nombrado Sin nombre Con nombre local Con nombre de red Identificador interno Identificador propio Descriptor de fichero Flujo de datos Unidireccional Bidireccional Buffering Sin buffering Con buffering Sincronización Síncrono (bloqueante) Asíncrono (no bloqueante)

13 Problemas clásicos de comunicación y sincronización
Productor-consumidor: Lectores-escritores:

14 Problemas clásicos de comunicación y sincronización (II)
Comunicación cliente-servidor: Computadora Computadora Petición Proceso Proceso cliente servidor S.O. Respuesta

15 Pipes int pipe(int fildes[2]); read(fildes[0],buffer,n)
Pipe vacío  se bloquea el lector Pipe con p bytes  Si p  n devuelve n Si p  n devuelve p Si pipe vacío y no hay escritores devuelve 0 write(fildes[1], buffer, n) Pipe lleno  se bloquea el escritor Si no hay lectores se recibe la señal SIGPIPE Lecturas y escrituras atómicas (cuidado con tamaños grandes) Mecanismo de comunicación y sincronización sin nombre Sólo puede utilizarse entre los procesos hijos del proceso que creó el pipe int pipe(int fildes[2]); Identificación: dos descriptores de fichero Flujo de datos: unidireccional Con buffering

16 Secciones críticas con pipes
void main(void) { int fildes[2]; /* pipe sincronización */ char c; /* carácter sincronización */ pipe(fildes); write(fildes[1], &c, 1); /* necesario para entrar en la seccion crítica la primera vez */ if (fork() == 0) { /* proceso hijo */ for(;;) read(fildes[0], &c, 1); /* entrada sección crítica */ /* sección crítica */ /* salida seccion crítica */ } else /* proceso padre */ { for(;;) read(fildes[0], &c, 1); /* entrada sección crítica*/ /* sección crítica */ write(fildes[1], &c, 1); /* salida sección crítica */ }

17 Productor-consumidor con pipes
void main(void) { int fildes[2]; /* pipe para comunicar y sincronizar */ int dato_p[4]; /* datos a producir */ int dato_c; /* dato a consumir */ pipe(fildes); if (fork() == 0) /* productor */ for(;;) /* producir dato_p */ write(fildes[1], dato_p, 4*sizeof(int)); } else /* consumidor */ { for(;;) read(fildes[0], &dato_c, sizeof(int)); /* consumir dato */ } Proceso hijo padre pipe SO write read Flujo de datos

18 Ejecución de mandatos con pipes
/* programa que ejecuta el mandato “ls | wc” */ void main(void) { int fd[2]; pid_t pid; if (pipe(fd) < 0) { perror(``pipe''); exit(1); } pid = fork(); switch(pid) { case -1: /* error */ perror(``fork''); case 0: /* proceso hijo ejecuta “ls” */ close(fd[0]); /* cierra el pipe de lectura */ close(STDOUT_FILENO); /* cierra la salida estandar */ dup(fd[1]); close(fd[1]); execlp(``ls'',``ls'',NULL); perror(``execlp''); exit(1); default: /* proceso padre ejecuta “wc” */ close(fd[1]); /* cierra el pipe de escritura */ close(STDIN_FILENO); /* cierra la entrada estandar */ dup(fd[0]); close(fd[0]); execlp(``wc'',``wc'',NULL); }

19 Ejecución de mandatos con pipes (II)

20 FIFOS Servicios Igual que los pipes
Mecanismo de comunicación y sincronización con nombre Misma máquina Servicios mkfifo(char *name,mode_t mode); Crea un FIFO con nombre name open(char *name, int flag); Abre un FIFO (para lectura, escritura o ambas) Bloquea hasta que haya algún proceso en el otro extremo Lectura y escritura mediante read() y write() Igual semántica que los pipes Cierre de un FIFO mediante close() Borrado de un FIFO mediante unlink()

21 Semáforos s_espera(s); /* entrada en la seccion critica */
Mecanismo de sincronización Misma máquina Objeto con un valor entero Dos operaciones atómicas s_espera(s) { s = s - 1; if (s < 0) Bloquear al proceso } s_abre(s) s = s + 1; if (s <= 0) Desbloquear a un proceso bloqueado Secciones críticas con semáforos: s_espera(s); /* entrada en la seccion critica */ /* seccion critica */ s_abre(s); /* salida de la seccion critica */ El semáforo debe tener valor inicial 1

22 Semáforos POSIX Inicializa un semáforo sin nombre
sem_init(sem_t *sem, int shared, int val); Inicializa un semáforo sin nombre int sem_destroy(sem_t *sem); Destruye un semáforo sin nombre sem_t *sem_open(char *name,int flag,mode_t mode,int val); Abre (crea) un semáforo con nombre. int sem_close(sem_t *sem); Cierra un semáforo con nombre. int sem_unlink(char *name); Borra un semáforo con nombre. int sem_wait(sem_t *sem); Realiza la operación s_espera sobre un semáforo. int sem_post(sem_t *sem); Realiza la operación s_abre sobre un semáforo.

23 Productor-consumidor con semáforos (buffer acotado y circular)

24 Productor-consumidor con semáforos (II)
/* crear los procesos ligeros */ pthread_create(&th1, NULL, Productor, NULL); pthread_create(&th2, NULL, Consumidor, NULL); /* esperar su finalizacion */ pthread_join(th1, NULL); pthread_join(th2, NULL); sem_destroy(&huecos); sem_destroy(&elementos); sem_destroy(&mutex); exit(0); } #define MAX_BUFFER 1024 #define DATOS_A_PRODUCIR sem_t elementos; /* elementos buffer */ sem_t huecos; /* huecos buffer */ sem_t mutex; /* controla excl.mutua */ int buffer[MAX_BUFFER];/*buffer común*/ void main(void) { pthread_t th1, th2; /* id. threads*/ /* inicializar los semaforos */ sem_init(&elementos, 0, 0); sem_init(&huecos, 0, MAX_BUFFER); sem_init(&mutex, 0, 1);

25 Productor-consumidor con semáforos (III)
void Productor(void) /* código del productor */ { int pos = 0; /* posición dentro del buffer */ int dato; /* dato a producir */ int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ sem_wait(&huecos); /* un hueco menos */ sem_wait(&mutex); /* entra en la sección crítica */ buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER; sem_post(&mutex); /* deja la sección crítica */ sem_post(&elementos); /* un elemento más */ } pthread_exit(0);

26 Productor-consumidor con semáforos (IV)
void Consumidor(void) /* código del Consumidor */ { int pos = 0; int dato; int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(&elementos); /* un elemento menos */ sem_wait(&mutex); /* entra en la sección crítica */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; sem_post(&mutex); /* deja la sección crítica */ sem_post(&huecos); /* un hueco más */ /* consumir dato */ } pthread_exit(0);

27 Lectores-escritores con semáforos
int dato = 5; /* recurso */ int n_lectores = 0;/* num. Lectores */ sem_t sem_lec; /* acceso n_lectores */ sem_t mutex; /* acceso a dato */ void main(void) { pthread_t th1, th2, th3, th4; sem_init(&mutex, 0, 1); sem_init(&sem_lec, 0, 1); pthread_create(&th1, NULL, Lector, NULL); pthread_create(&th2, NULL, Escritor, NULL); pthread_create(&th3, NULL, Lector, NULL); pthread_create(&th4, NULL, Escritor, NULL); pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_join(th3, NULL); pthread_join(th4, NULL); /*cerrar todos los semaforos*/ sem_destroy(&mutex); sem_destroy(&sem_lec); exit(0); }

28 Lectores-escritores con semáforos (II)
void Lector(void) /* código del lector */ { sem_wait(&sem_lec); n_lectores = n_lectores + 1; if (n_lectores == 1) sem_wait(&mutex); sem_post(&sem_lec); printf(``%d\n'', dato); /* leer dato */ n_lectores = n_lectores - 1; if (n_lectores == 0) sem_post(&mutex); pthread_exit(0); }

29 Lectores-escritores con semáforos (III)
void Escritor(void) /* código del escritor */ { sem_wait(&mutex); dato = dato + 2; /* modificar el recurso */ sem_post(&mutex); pthread_exit(0); }

30 Objetos de memoria compartida
Permiten crear un segmento de memoria compartido entre procesos de la misma máquina. Declaración independiente de variables

31 Servicios int shm_open(char *name, int oflag, mode_t mode);
Crea un objeto de memoria a compartir entre procesos int shm_open (char *name, int oflag); Sólo apertura int close(int fd); Cierre. El objeto persiste hasta que es cerrado por el último proceso. int shm_unlink(const char *name); Borra una zona de memoria compartida. void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); Establece una proyección entre el espacio de direcciones de un proceso y un descriptor de fichero u objeto de memoria compartida.

32 Servicios (II) void munmap(void *addr, size_t len);
len especifica el número de bytes a proyectar. prot el tipo de acceso (lectura, escritura o ejecución). PROT_READ, PROT_WRITE, PROT_EXEC o PROT_NONE. flags especifica información sobre el manejo de los datos proyectados (compartidos, privado, etc.). MAP_SHARED, MAP_PRIVATE, ... fildes representa el descriptor de fichero del fichero o descriptor del objeto de memoria a proyectar. off desplazamiento dentro del fichero a partir de cual se realiza la proyección. void munmap(void *addr, size_t len); Desproyecta parte del espacio de direcciones de un proceso comenzando en la dirección addr. El tamaño de la región a desproyectar es len.

33 Productor-consumidor con objetos de memoria compartida
Crea la zona de memoria compartida (shm_open) Le asigna espacio (ftruncate) int ftruncate (int fd, size_t len); Proyecta la zona de memoria compartida en su espacio de direcciones (mmap) Utiliza la zona de memoria compartida Desproyecta la zona de memoria compartida Cierra y borra el objeto de memoria compartida

34 Productor-consumidor con objetos de memoria compartida (II)
Debe esperar a que la zona de memoria compartida este creada Abre la zona de memoria compartida (shm_open) Proyecta la zona de memoria compartida en su espacio de direcciones (mmap) Utiliza la zona de memoria compartida Cierra el objeto de memoria compartida

35 Código del productor #define MAX_BUFFER 1024 /* tamaño del buffer */
#define DATOS_A_PRODUCIR /* datos a producir */ sem_t *elementos; /* elementos en el buffer */ sem_t *huecos; /* huecos en el buffer */ sem_t *mutex; /* acceso al buffer */ void main(int argc, char *argv[]) { int shd; int *buffer; /* buffer común */ /* el productor crea el segmento de memoria compartida */ shd = shm_open("BUFFER", O_CREAT|O_WRONLY, 0700); ftruncate(shd, MAX_BUFFER * sizeof(int)); /* proyectar el objeto de memoria compartida en el espacio de direcciones del productor */

36 Código del productor (II)
buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int), PROT_WRITE, MAP_SHARED, shd, 0); /* El productor crea los semáforos */ elementos = sem_open("ELEMENTOS", O_CREAT, 0700, 0); huecos = sem_open("HUECOS", O_CREAT, 0700, MAX_BUFFER); mutex = sem_open("MUTEX", O_CREAT, 0700, 1); Productor(buffer); /* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */ shm_unlink("BUFFER"); /* borrar el objeto de memoria */ sem_close(elementos); sem_close(huecos); sem_close(mutex); sem_unlink("ELEMENTOS"); sem_unlink("HUECOS"); sem_unlink("MUTEX"); }

37 Código del productor (III)
void Productor(int *buffer){ /* código del productor */ int pos = 0; /* posición dentro del buffer */ int dato; /* dato a producir */ int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ sem_wait(huecos); /* un hueco menos */ sem_wait(mutex); buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER; sem_post(mutex); sem_post(elementos); /* un elemento más */ } return;

38 Código del consumidor #define MAX_BUFFER 1024 /* tamaño del buffer */
#define DATOS_A_PRODUCIR /* datos a producir */ sem_t *elementos; /* elementos en el buffer */ sem_t *huecos; /* huecos en el buffer */ sem_t *mutex; /* acceso al buffer */ void main(int argc, char *argv[]) { int shd; int *buffer; /* buffer común */ /* el consumidor abre el segmento de memoria compartida */ shd = shm_open("BUFFER", O_RDONLY); /* proyectar el objeto de memoria compartida en el espacio de direcciones del productor */

39 Código del consumidor (II)
buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int), PROT_READ, MAP_SHARED, shd, 0); /* El consumidor abre los semáforos */ elementos = sem_open("ELEMENTOS", 0); huecos = sem_open("HUECOS", 0); mutex = sem_open("MUTEX", 0); Consumidor(buffer); /* desproyectar el buffer compartido */ munmap(buffer, MAX_BUFFER * sizeof(int)); close(shd); /* cerrar el objeto de memoria compartida */ /* cerrar los semáforos */ sem_close(elementos); sem_close(huecos); sem_close(mutex); }

40 Código del consumidor (III)
void Consumidor(char *buffer) /*código del Consumidor*/ { int pos = 0; int i, dato; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { sem_wait(elementos); /* un elemento menos */ sem_wait(mutex); dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; sem_post(mutex); sem_post(huecos); /* un hueco mas */ printf("Consume %d \n", dato); /* consumir dato */ } return;

41 Mutex y variables condicionales
Un mutex es un mecanismo de sincronización indicado para procesos ligeros. Es un semáforo binario con dos operaciones atómicas: lock(m): Intenta bloquear el mutex; si el mutex ya está bloqueado el proceso se suspende. unlock(m): Desbloquea el mutex; si existen procesos bloqueados en el mutex se desbloquea a uno.

42 Secciones críticas con mutex
lock(m); /* entrada en la sección crítica */ /* sección crítica */ unlock(s); /* salida de la sección crítica */ La operación unlock debe realizarla el proceso ligero que ejecutó lock

43 Variables condicionales
Variables de sincronización asociadas a un mutex Conveniente ejecutarlas entre lock y unlock. Dos operaciones atómicas: wait: Bloquea al proceso ligero que la ejecuta y le expulsa del mutex signal: Desbloquea a uno o varios procesos suspendidos en la variable condicional. El proceso que se despierta compite de nuevo por el mutex

44 Uso de mutex y variables condicionales
Proceso ligero A: lock(mutex); /* acceso al recurso */ /* codigo de la sección crítica */ while (condicion == FALSE) wait(condition, mutex); /* resto de la sección crítica */ unlock(mutex); Proceso ligero B lock(mutex); /* acceso al recurso */ /* código de la sección crítica */ /* se modifica la condicción y ésta se hace TRUE */ condicion = true; signal(condition, mutex); unlock(mutex); Importante utilizar while

45 Servicios int pthread_mutex_init(pthread_mutex_t *mutex,
pthread_mutexattr_t *attr) Inicializa un mutex. int pthread_mutex_destroy(pthread_mutex_t *mutex); Destruye un mutex. int pthread_mutex_lock(pthread_mutex_t *mutex); Intenta obtener el mutex. Bloquea al proceso ligero si el mutex se encuentra adquirido por otro proceso ligero. int pthread_mutex_unlock(pthread_mutex_t *mutex); Desbloquea el mutex. int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr); Inicializa una variable condicional. int pthread_cond_destroy(pthread_cond_t *cond); Destruye una variable condicional.

46 Servicios (II) int pthread_cond_signal(pthread_cond_t *cond);
Se reactivan uno o más de los procesos ligeros que están suspendidos en la variable condicional cond. No tiene efecto si no hay ningún proceso ligero esperando (diferente a los semáforos). int pthread_cond_broadcast(pthread_cond_t *cond); Todos los threads suspendidos en la variable condicional cond se reactivan. No tiene efecto si no hay ningún proceso ligero esperando. int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); Suspende al proceso ligero hasta que otro proceso señaliza la variable condicional cond. Automáticamente se libera el mutex. Cuando se despierta el proceso ligero vuelve a competir por el mutex.

47 Productor-consumidor con mutex
#define MAX_BUFFER /* tamaño del buffer */ #define DATOS_A_PRODUCIR /* datos a producir */ pthread_mutex_t mutex; /* mutex para controlar el acceso al buffer compartido */ pthread_cond_t no_lleno; /* llenado del buffer */ pthread_cond_t no_vacio; /* vaciado del buffer */ int n_elementos; /* elementos en el buffer */ int buffer[MAX_BUFFER]; /* buffer común */ main(int argc, char *argv[]) { pthread_t th1, th2; pthread_mutex_init(&mutex, NULL);

48 Productor-consumidor con mutex (II)
pthread_cond_init(&no_lleno, NULL); pthread_cond_init(&no_vacio, NULL); pthread_create(&th1, NULL, Productor, NULL); pthread_create(&th2, NULL, Consumidor, NULL); pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&no_lleno); pthread_cond_destroy(&no_vacio); exit(0); }

49 Productor-consumidor con mutex (III)
void Productor(void) { /* código del productor */ int dato, i ,pos = 0; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { dato = i; /* producir dato */ pthread_mutex_lock(&mutex); /* acceder al buffer */ while (n_elementos==MAX_BUFFER)/* si buffer lleno */ pthread_cond_wait(&no_lleno, &mutex);/* bloqueo */ buffer[pos] = dato; pos = (pos + 1) % MAX_BUFFER; n_elementos ++; pthread_cond_signal(&no_vacio);/* buffer no vacío */ pthread_mutex_unlock(&mutex); } pthread_exit(0);

50 Productor-consumidor con mutex (IV)
void Consumidor(void) { /* código del consumidor */ int dato, i ,pos = 0; for(i=0; i < DATOS_A_PRODUCIR; i++ ) { pthread_mutex_lock(&mutex); /* acceder al buffer */ while (n_elementos == 0) /* si buffer vacío */ pthread_cond_wait(&no_vacio, &mutex);/* bloqueo */ dato = buffer[pos]; pos = (pos + 1) % MAX_BUFFER; n_elementos --; pthread_cond_signal(&no_lleno); /* buffer no lleno */ pthread_mutex_unlock(&mutex); printf("Consume %d \n", dato); /* consume dato */ } pthread_exit(0);

51 Lectores-escritores con mutex
int dato = 5; /* recurso */ int n_lectores = 0; /* numero de lectores */ pthread_mutex_t mutex; /* control del acceso a dato */ pthread_mutex_t mutex_lectores; /* control de n_lectores */ main(int argc, char *argv[]) { pthread_t th1, th2, th3, th4; pthread_mutex_init(&mutex, NULL); pthread_mutex_init(&mutex_lectores, NULL);

52 Lectores-escritores con mutex (II)
pthread_create(&th1, NULL, Lector, NULL); pthread_create(&th2, NULL, Escritor, NULL); pthread_create(&th3, NULL, Lector, NULL); pthread_create(&th4, NULL, Escritor, NULL); pthread_join(th1, NULL); pthread_join(th2, NULL); pthread_join(th3, NULL); pthread_join(th4, NULL); pthread_mutex_destroy(&mutex); pthread_mutex_destroy(&mutex_lectores); exit(0); }

53 Lectores-escritores con mutex (III)
void Lector(void) { /* código del lector */ pthread_mutex_lock(&mutex_lectores); n_lectores++; if (n_lectores == 1) pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex_lectores); printf("%d\n", dato); /* leer dato */ n_lectores--; if (n_lectores == 0) pthread_mutex_unlock(&mutex); pthread_exit(0); }

54 Lectores-escritores con mutex (IV)
void Escritor(void) /* código del escritor */ { pthread_mutex_lock(&mutex); dato = dato + 2; /* modificar el recurso */ pthread_mutex_unlock(&mutex); pthread_exit(0); }

55 Paso de mensajes Múltiples soluciones Permite resolver:
Paso de mensajes en POSIX (colas de mensajes): Misma máquina (uniprocesador, multiprocesador o multicomputador) Mecanismo de nombrado: con nombre local Identificación: identificador especial Buffering: Sí Unidireccional Sincronización: bloqueante y no bloqueante Permite resolver: Exclusión mutua Sincronizar entre un proceso que recibe un mensaje y otro que lo envía Comunicación de datos entre espacios de memoria diferentes (mismo computador, diferentes computadores) Primitivas básicas: send(destino, mensaje): envía un mensaje al proceso destino receive(origen, mensaje): recibe un mensaje del proceso origen

56 Colas de mensajes POSIX
mqd_t mq_open(char *name, int flag, mode_t mode, mq_attr *attr); Crea una cola de mensajes con nombre y atributos attr: Número máximo de mensajes. Tamaño máximo del mensaje. Bloqueante, No bloqueante. int mq_close (mqd_t mqdes); Cierra una cola de mensajes. int mq_unlink(char *name); Borra una cola de mensajes.

57 Colas de mensajes POSIX (II)
int mq_send(mqd_t mqdes, char *msg, size_t len, int prio); Envía el mensaje msg de longitud len a la cola de mensajes mqdes con prioridad prio; Si la cola está llena el envío puede ser bloqueante o no. int mq_receive(mqd_t mqdes, char *msg, size_t len, int *prio); Recibe un mensaje msg de longitud len de la cola de mensajes mqdes (el de mayor prioridad). Almacena la prioridad del mensaje en el parámetro prio; Recepción bloqueante o no.

58 Secciones críticas con colas de mensajes
void main(void){ mqd_t mutex; /* cola de mensajes para sincronizar acceso a la sección crítica */ struct mq_attr attr; /*atributos cola de mensajes*/ char c; /* carácter para sincronizar */ attr.mq_maxmsg = 1; /* número máximo mensajes */ attr.mq_msgsize = 1; /* tamaño mensaje */ mutex = mq_open(``MUTEX'', O_CREAT|O_RDWR, 0777, &attr); mq_send(mutex, &c, 1, 0); /* entrar en la sección crítica la primera vez */

59 Secciones críticas con colas de mensajes (II)
if (fork() == 0) { /* proceso hijo */ for(;;){ mq_receive(mutex, &c, 1, NULL);/*entrada sec. crítica */ /* sección crítica */ mq_send(mutex, &c, 1, 0); /* salida sec. critica */ } }else { /* proceso padre */

60 Productor-consumidor con colas de mensajes
#define MAX_BUFFER /* tamaño del buffer */ #define DATOS_A_PRODUCIR /*datos a producir*/ mqd_t almacen; /* cola de mensaje para almacenamiento de datos */ void main(void) { struct mq_attr attr; attr.mq_maxmsg = MAX_BUFFER; attr.mq_msgsize = sizeof(int); almacen = mq_open("ALMACEN", O_CREAT|O_RDWR, 0777, &attr);

61 Productor-consumidor con colas de mensajes (II)
if (almacen == -1) { perror("mq_open"); exit(1); } if (fork() == 0) /* proceso hijo */ Productor(); else /* proceso padre */ Consumidor(); exit(0);

62 Productor-consumidor con colas de mensajes (III)
void Consumidor(void){ int dato; int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ){ mq_receive(almacen, &dato, sizeof(int), NULL); /* consumir dato */ printf("Consume %d\n", dato); } return; void Productor(void) { int dato; int i; for(i=0; i < DATOS_A_PRODUCIR; i++ ){ /* producir dato */ dato = i; printf("Produce %d \n", dato); mq_send(almacen, &dato, sizeof(int), 0); } return;

63 Lectores-escritores con colas de mensajes
struct peticion_t{ int tipo; char cola[100]; }; void lector(char *lector) { /* identificador lector */ mqd_t controlador; /* cola proceso controlador */ mqd_t cola_local; /* cola para el lector */ struct mq_attr attr; struct peticion_t peticion; attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct peticion_t); controlador = mq_open("CONTROLADOR", O_RDWR); cola_local = mq_open(lector, O_CREAT|O_RDWR, 0777, &attr);

64 Lectores-escritores con colas de mensajes (II)
peticion.tipo = READ; strcpy(peticion.cola, lector); /* solicitud de lectura al controlador */ mq_send(controlador, &peticion, sizeof(peticion), 0); mq_receive(cola_local, &peticion, sizeof(peticion), NULL); /* leer */ peticion.tipo = OK; /* fin de lectura */ mq_send(cola_local, &peticion, sizeof(peticion), 0); mq_close(cola_local); mq_close(controlador); mq_unlink(lector); } El proceso escritor es similar.

65 Lectores-escritores con colas de mensajes (III)
void Controlador(void){ mqd_t controlador; /* cola del proceso controlador */ struct mq_attr attr; struct peticion_t peticion; attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct peticion_t); controlador = mq_open("CONTROLADOR", O_CREAT|O_RDWR, , &attr); for (;;) { mq_receive(controlador, &peticion, sizeof(peticion), 0); if (peticion.tipo == READ) crear_thread(do_read, &peticion); else crear_thread(do_write, &peticion); }

66 Lectores-escritores con colas de mensajes (IV)
void do_write(struct peticion_t *pet) { mqd_t cola_local; struct peticion_t peticion; /* abre la cola del proceso que realiza la peticion */ cola_local = mq_open(pet->cola, O_RDWR); pthread_mutex_lock(mutex); while (n_lectores > 0) pthread_cond_wait(&no_lectores, &mutex); peticion.tipo = OK; mq_send(cola_local, peticion, sizeof(peticion), 0); mq_receive(cola_local, peticion, sizeof(peticion), NULL); pthread_mutex_unlock(mutex); } void do_read(struct peticion_t *pet) { mqd_t cola_local; struct peticion_t peticion; /* abre la cola del proceso que realiza la peticion */ cola_local = mq_open(pet->cola, O_RDWR); pthread_mutex_lock(mutex); n_lectores ++; peticion.tipo = OK; mq_send(cola_local, peticion, sizeof(peticion), 0); mq_receive(cola_local, peticion, sizeof(peticion), NULL); n_lectores --; if (n_lectores == 0) pthread_cond_signal(&no_lectores); pthread_mutex_unlock(mutex); }

67 Servidor multithread con colas de mensajes

68 Servidor multithread con colas de mensajes (II)
/* estructura de un mensaje */ struct mensaje { char buffer[1024]; /* datos a enviar */ char cliente[256]; /* cola del cliente */ }; /* mutex y var. condicionales protección de la copia del mensaje */ pthread_mutex_t mutex_mensaje; int mensaje_no_copiado = TRUE; pthread_cond_t cond; void main(void) { mqd_t q_servidor; /* cola del servidor */ struct mensaje mess; /* mensaje a recibir */ struct mq_attr q_attr; /* atributos de la cola */ pthread_attr_t t_attr; /* atributos de los threads */ q_attr.mq_maxmsg = 20; q_attr.mq_msgsize = sizeof(struct mensaje);

69 Servidor multithread con colas de mensajes (III)
q_servidor = mq_open("SERVIDOR", O_CREAT|O_RDONLY, 0700, &q_attr); pthread_mutex_init(&mutex_mensaje, NULL); pthread_cond_init(&cond, NULL); pthread_attr_init(&t_attr); pthread_attr_setscope(&t_attr,PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED); while (TRUE) { mq_receive(q_servidor, &mess, sizeof(struct mensaje), NULL); pthread_create(&thid, &t_attr, tratar_mensaje, &mess); /* se espera a que el thread copie el mensaje */ pthread_mutex_lock(&mutex_mensaje); while (mensaje_no_copiado) pthread_cond_wait(&cond, &mutex_mensaje); mensaje_no_copiado = TRUE; pthread_mutex_unlock(&mutex_mensaje); }

70 Servidor multithread con colas de mensajes (IV)
void tratar_mensaje(struct mensaje *mes){ struct mensaje mensaje_local; struct mqd_t q_cliente; /* cola del cliente */ struct mensaje respuesta; /* mensaje de respuesta al cliente */ /* el thread copia el mensaje */ pthread_mutex_lock(&mutex_mensaje); memcpy((char *) &mensaje_local, (char *)&mes, sizeof(struct mensaje)); /* ya se puede despertar al servidor*/ mensaje_no_copiado = FALSE; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex_mensaje); /* ejecutar la petición del cliente y preparar respuesta */ /* responder al cliente a su cola */ q_cliente = mq_open(mensaje_local.cliente, O_WRONLY); mqsend(q_cliente, (char *) &respuesta, sizeof(respuesta), 0); mq_close(q_cliente); pthread_exit(0); }

71 Ejemplo de código cliente
struct mensaje {/* estructura mensaje */ char buffer[1024]; /* datos envío */ char cliente[256]; /* cola cliente */ }; void main(void) { mqd_t q_servidor; /* cola servidor */ mqd_t q_cliente; /* cola cliente */ struct mq_attr attr; /* atr. cola */ struct mensaje peticion; /* peticion al servidor */ struct mensaje respuesta; /* respuesta del servidor */ attr.mq_maxmsg = 1; attr.mq_msgsize = sizeof(struct mensaje); q_cliente = mq_open("CLIENTE", O_CREAT|O_RDONLY, 0700, 0); q_servidor = mq_open("SERVIDOR", O_WRONLY); /* preparar peticion */ mq_send(q_servidor, &peticion, sizeof(struct mensaje), 0); /* esperar respuesta */ mq_receive(q_cliente, &respuesta, sizeof(struct mensaje), NULL); mq_close(q_servidor); mq_close(q_cliente); mq_unlink("CLIENTE"); exit(0); }

72 Sockets Permiten comunicar: Tipos de direcciones:
Procesos del mismo computador Procesos conectados a través de una red Tipos de direcciones: Direcciones locales: dominio UNIX Direcciones de red (TCP/IP) Dirección de red (dirección IP) Puerto

73 Sockets (II) merlin 138.100.240.50 conan 138.100.240.20
Red de interconexión ftp 21 telnet 23 patan zipi batman

74 Resumen Pipes: FIFOS: Mecanismo de comunicación y sincronización
Mecanismo de nombrado: sin nombre Identificación: descriptor de fichero Flujo de datos: unidireccional Buffering: Sí Sincronización: Si pipe vacío  read() se bloquea Si pipe lleno  write() se bloquea FIFOS: Mecanismo de comunicación y sincronización Mecanismo de nombrado: con nombre local Identificación: descriptor de fichero Flujo de datos: unidireccional Buffering: Sí Sincronización: Si FIFO vacío  read() se bloquea Si FIFO lleno  write() se bloquea

75 Resumen (II) Semáforos POSIX: Mutex y variables condicionales:
Mecanismo de sincronización Mecanismo de nombrado: sin nombre y con nombre local Identificación: variable de tipo semáforo Mutex y variables condicionales: Mecanismo de sincronización adecuado para procesos ligeros Mecanismo de nombrado: sin nombre Identificación: variables Colas de mensajes POSIX: Mecanismo de comunicación y sincronización Mecanismo de nombrado: con nombre local Identificación: identificador especial Buffering: Sí Unidireccional Sincronización: bloqueante y no bloqueante

76 Resumen (III) Objetos de memoria compartida: Sockets:
Mecanismo de comunicación Mecanismo de nombrado: con nombre local Identificación: identificador especial Sockets: Mecanismo de comunicación y sincronización Mecanismo de nombrado: con nombre de red Identificación: descriptor de fichero Buffering: Sí Sincronización: bloqueante y no bloqueante

77 Interbloqueos Bloqueo permanente de un conjunto de procesos que compiten por los recursos del sistema o se comunican entre sí. Ejemplo: Si S y Q con semáforos con valor inicial P P1 s_espera(S) s_espera(Q) s_espera(Q) s_espera(S) s_abre(S) s_abre(Q) s_abre(Q) s_abre(S) Ejemplo: Si C1 y C2 son dos colas de mensajes P P1 receive(C1, M) receive(C2, N) send(C2, M) send(C1, N) Condiciones del interbloqueo: Exclusión mutua Retención y espera No apropiación Espera circular

78 Ejemplo Ejemplo: Si P y Q son semáforos con valor inicial 1
A B s_espera(P) s_espera(Q) s_espera(Q) s_espera(P) Interbloqueo

79 Métodos para tratar con los interbloqueos
Ignorarlos  UNIX Solucionarlos: Métodos para prevenir los interbloqueos Métodos para evitar los interbloqueos Métodos para detectar los interbloqueos


Descargar ppt "Comunicación y sincronización entre procesos"

Presentaciones similares


Anuncios Google