79
Comunicación y sincronización entre procesos María de los Santos Pérez Hernández [email protected]

Comunicación y sincronización entre procesos

  • Upload
    nemo

  • View
    139

  • Download
    1

Embed Size (px)

DESCRIPTION

Comunicación y sincronización entre procesos. María de los Santos Pérez Hernández [email protected]. Í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. - PowerPoint PPT Presentation

Citation preview

Page 1: Comunicación y sincronización entre procesos

Comunicación y sincronización entre procesos

María de los Santos Pérez Hernández

[email protected]

Page 2: Comunicación y sincronización entre procesos

[email protected] 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.

Page 3: Comunicación y sincronización entre procesos

[email protected] 3

Referencias bibliográficas “Sistemas Operativos: una visión aplicada”

Jesús Carretero et al.McGraw Hill, 2001

“Sistemas Operativos” Willian StallingWillian Stalling Prentice Hall, 1997

“Operating System Concepts” A. Silberschatz, P. GalvinAddison-Wesley, 1998

Page 4: Comunicación y sincronización entre procesos

[email protected] 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

Page 5: Comunicación y sincronización entre procesos

[email protected] 5

Procesos concurrentes (II)

Tipos de procesos Independientes Cooperantes

Interacción entre procesos Compiten por recursos Comparten recursos

Page 6: Comunicación y sincronización entre procesos

[email protected] 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

Page 7: Comunicación y sincronización entre procesos

[email protected] 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

Page 8: Comunicación y sincronización entre procesos

[email protected] 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

Page 9: Comunicación y sincronización entre procesos

[email protected] 9

Ejemplo con sección críticavoid 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

Page 10: Comunicación y sincronización entre procesos

[email protected] 10

Mecanismos de comunicación

Ficheros Pipes FIFOS Variables en memoria compartida Paso de mensajes Sockets

Page 11: Comunicación y sincronización entre procesos

[email protected] 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

Page 12: Comunicación y sincronización entre procesos

[email protected] 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)

Page 13: Comunicación y sincronización entre procesos

[email protected] 13

Problemas clásicos de comunicación y sincronización

ProcesoProductor

ProcesoConsum idor

M ecanism o de com unicación

Flu jo de datos

• Productor-consumidor:

EscritorLector Lector Escritor Lector

Recurso

• Lectores-escritores:

Page 14: Comunicación y sincronización entre procesos

[email protected] 14

Problemas clásicos de comunicación y sincronización (II)

• Comunicación cliente-servidor:

Computadora Computadora

Procesocliente

S.O.

Petición

Respuesta

Procesoservidor

Page 15: Comunicación y sincronización entre procesos

[email protected] 15

Pipes 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

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)

Page 16: Comunicación y sincronización entre procesos

[email protected] 16

Secciones críticas con pipesvoid 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 seccioncrítica la primera vez */

if (fork() == 0) { /* proceso hijo */ for(;;)

{ read(fildes[0], &c, 1);

/* entrada sección crítica */ /* sección crítica */ write(fildes[1], &c, 1);

/* 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

*/

}

}

}

Page 17: Comunicación y sincronización entre procesos

[email protected] 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

hijoProceso

padre

pipe

SO

write read

Flujo de datos

Page 18: Comunicación y sincronización entre procesos

[email protected] 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''); exit(1);

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); perror(``execlp''); } }

Page 19: Comunicación y sincronización entre procesos

[email protected] 19

Ejecución de mandatos con pipes (II)

Proceso

S TD INS TD O UT

pipe(fd)

fork()

Proceso

S TD INS TD O UTfd[0]fd [1 ]

pipe

red irección

P roceso h ijo

S TD INS TD O UTfd[0]fd [1 ]

P roceso

S TD INS TD O UTfd[0]fd [1 ]

pipe

exec

Proceso hijo

S TD IN

S TD O UT

Proceso

S TD IN

S TD O UTpipe

Proceso hijo

S TD IN

S TD O UT

Proceso

ls wc

S TD IN

S TD O UTpipe

Page 20: Comunicación y sincronización entre procesos

[email protected] 20

FIFOS

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()

Page 21: Comunicación y sincronización entre procesos

[email protected] 21

Semáforos 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

Page 22: Comunicación y sincronización entre procesos

[email protected] 22

Semáforos POSIX 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.

Page 23: Comunicación y sincronización entre procesos

[email protected] 23

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

Consum idor

Productor

Page 24: Comunicación y sincronización entre procesos

[email protected] 24

Productor-consumidor con semáforos (II)

#define MAX_BUFFER 1024 #define DATOS_A_PRODUCIR 100000sem_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);

/* 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);}

Page 25: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 26: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 27: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 28: Comunicación y sincronización entre procesos

[email protected] 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 */

sem_wait(&sem_lec); n_lectores = n_lectores - 1; if (n_lectores == 0) sem_post(&mutex); sem_post(&sem_lec);

pthread_exit(0);}

Page 29: Comunicación y sincronización entre procesos

[email protected] 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);

}

Page 30: Comunicación y sincronización entre procesos

[email protected] 30

Objetos de memoria compartida Permiten crear un

segmento de memoria compartido entre procesos de la misma máquina.

Declaración independiente de variables

D atos

Texto

Proceso A Proceso B

P ila

Texto

D atos

P ilaSegm ento de m em oriacom partida

var1

*var1=2

2

var2

Page 31: Comunicación y sincronización entre procesos

[email protected] 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.

Page 32: Comunicación y sincronización entre procesos

[email protected] 32

Servicios (II) 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.

Page 33: Comunicación y sincronización entre procesos

[email protected] 33

Productor-consumidor con objetos de memoria compartida Productor:

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 espaciode direcciones (mmap)

Utiliza la zona de memoria compartida Desproyecta la zona de memoria compartida Cierra y borra el objeto de memoria compartida

Page 34: Comunicación y sincronización entre procesos

[email protected] 34

Productor-consumidor con objetos de memoria compartida (II)

Consumidor: 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

Page 35: Comunicación y sincronización entre procesos

[email protected] 35

Código del productor#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* 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 */

Page 36: Comunicación y sincronización entre procesos

[email protected] 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"); }

Page 37: Comunicación y sincronización entre procesos

[email protected] 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;}

Page 38: Comunicación y sincronización entre procesos

[email protected] 38

Código del consumidor#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* 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 */

Page 39: Comunicación y sincronización entre procesos

[email protected] 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); }

Page 40: Comunicación y sincronización entre procesos

[email protected] 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;}

Page 41: Comunicación y sincronización entre procesos

[email protected] 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.

Page 42: Comunicación y sincronización entre procesos

[email protected] 42

Secciones críticas con mutexlock(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 P roceso ligero A

lock m utex

Seccióncrítica

P roceso ligero B

lock m utex

unlock m utex obtiene m utex

Proceso ligero e jecutando

Proceso ligero b loqueado

Punto de sincronización

Page 43: Comunicación y sincronización entre procesos

[email protected] 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

Proceso ligero B

Proceso ligero A

w a itunlock mutex

Adquiere el mutex

Adquiere el mutex

Se compite por el mutex

locklock

un lock

Proceso ligero bloqueado esperando signal

Proceso ligero b loqueado esperando unlock

signa l

Page 44: Comunicación y sincronización entre procesos

[email protected] 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

Page 45: Comunicación y sincronización entre procesos

[email protected] 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.

Page 46: Comunicación y sincronización entre procesos

[email protected] 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.

Page 47: Comunicación y sincronización entre procesos

[email protected] 47

Productor-consumidor con mutex

#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /* 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);

Page 48: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 49: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 50: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 51: Comunicación y sincronización entre procesos

[email protected] 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);

Page 52: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 53: Comunicación y sincronización entre procesos

[email protected] 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 */

pthread_mutex_lock(&mutex_lectores); n_lectores--; if (n_lectores == 0) pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex_lectores);

pthread_exit(0);}

Page 54: Comunicación y sincronización entre procesos

[email protected] 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);

}

Page 55: Comunicación y sincronización entre procesos

[email protected] 55

Paso de mensajes 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

Múltiples soluciones 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

Page 56: Comunicación y sincronización entre procesos

[email protected] 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.

Page 57: Comunicación y sincronización entre procesos

[email protected] 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.

Page 58: Comunicación y sincronización entre procesos

[email protected] 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 */

Page 59: Comunicación y sincronización entre procesos

[email protected] 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 */ for(;;){ mq_receive(mutex, &c, 1, NULL);/*entrada sec. crítica */ /* sección crítica */ mq_send(mutex, &c, 1, 0); /* salida sec. critica */ } }}

Page 60: Comunicación y sincronización entre procesos

[email protected] 60

Productor-consumidor con colas de mensajes

#define MAX_BUFFER 1024 /* tamaño del buffer */#define DATOS_A_PRODUCIR 100000 /*datos a producir*/

mqd_t almacen; /* cola de mensaje para almacenamientode 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);

Page 61: Comunicación y sincronización entre procesos

[email protected] 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);

}

Page 62: Comunicación y sincronización entre procesos

[email protected] 62

Productor-consumidor con colas de mensajes (III)

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;}

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;}

Page 63: Comunicación y sincronización entre procesos

[email protected] 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);

Page 64: Comunicación y sincronización entre procesos

[email protected] 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.

Page 65: Comunicación y sincronización entre procesos

[email protected] 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, 0777, &attr);

for (;;) { mq_receive(controlador, &peticion, sizeof(peticion), 0); if (peticion.tipo == READ) crear_thread(do_read, &peticion); else crear_thread(do_write, &peticion); }}

Page 66: Comunicación y sincronización entre procesos

[email protected] 66

Lectores-escritores con colas de mensajes (IV)

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);}

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);}

Page 67: Comunicación y sincronización entre procesos

[email protected] 67

Servidor multithread con colas de mensajes

Proceso c liente

C ola de l c lien te

Proceso c liente

C ola de l c lien te

C ola de l serv idor

Proceso serv idor

petic ión

petic ión

respuestarespuesta

C reación de lth read

C reación de lth read

Thread quesirve la petic ión

Thread quesirve la petic ión

Page 68: Comunicación y sincronización entre procesos

[email protected] 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);

Page 69: Comunicación y sincronización entre procesos

[email protected] 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); }}

Page 70: Comunicación y sincronización entre procesos

[email protected] 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);}

Page 71: Comunicación y sincronización entre procesos

[email protected] 71

Ejemplo de código clientestruct 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);}

Page 72: Comunicación y sincronización entre procesos

[email protected] 72

Sockets

Permiten comunicar: 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

Page 73: Comunicación y sincronización entre procesos

[email protected] 73

Sockets (II)

merlin138.100.240.50

conan138.100.240.20

Red de interconexión

ftp21

telnet 23

patan138.100.240.36

zipi138.100.240.2

batman138.100.240.32

Page 74: Comunicación y sincronización entre procesos

[email protected] 74

Resumen Pipes:

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

Page 75: Comunicación y sincronización entre procesos

[email protected] 75

Resumen (II) Semáforos POSIX:

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

Page 76: Comunicación y sincronización entre procesos

[email protected] 76

Resumen (III) Objetos de memoria compartida:

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

Page 77: Comunicación y sincronización entre procesos

[email protected] 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

P0 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

P0 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

Page 78: Comunicación y sincronización entre procesos

[email protected] 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

Q

P

BA

Page 79: Comunicación y sincronización entre procesos

[email protected] 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