Seccion critica

Anuncio
Sistemas operativos: una visión aplicada
Capítulo 5
Comunicación y sincronización
de procesos
Contenido
•
•
•
•
•
•
•
Procesos concurrentes.
Problemas clásicos de comunicación y sincronización.
Mecanismos de comunicación y sincronización.
Paso de mensajes.
Aspectos de implementación
Interbloqueos.
Servicios POSIX
Sistemas operativos: una visión aplicada
2
© J. Carretero, F. García, P. de Miguel, F. Pérez
Procesos concurrentes
• 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
Sistemas operativos: una visión aplicada
3
© J. Carretero, F. García, P. de Miguel, F. Pérez
Sistema multiprogramado con un una
CPU
P ro c e so A
P ro c e so B
P ro c e so C
T ie m p o
Sistemas operativos: una visión aplicada
4
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejecución en un sistema multiprocesador
P ro c e so A
CPU 1
P ro c e so B
CPU 2
P ro c e so C
P ro c e so D
T ie m p o
Sistemas operativos: una visión aplicada
5
© J. Carretero, F. García, P. de Miguel, F. Pérez
Tipos de procesos concurrentes
• Tipos de procesos
– Independientes
– Cooperantes
• Interacción entre procesos
– Compiten por recursos
– Comparten recursos
Sistemas operativos: una visión aplicada
6
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
• Procesos concurrentes.
• Problemas clásicos de comunicación y sincronización.
•
•
•
•
•
Mecanismos de comunicación y sincronización.
Paso de mensajes.
Aspectos de implementación
Interbloqueos.
Servicios POSIX
Sistemas operativos: una visión aplicada
7
© J. Carretero, F. García, P. de Miguel, F. Pérez
Problemas clásicos de comunicación y sincronización
•
•
•
•
El problema de la sección crítica
El problema del productor-consumidor
El problema de los lectores-escritores
Comunicación cliente-servidor
Sistemas operativos: una visión aplicada
8
© J. Carretero, F. García, P. de Miguel, F. Pérez
Problema de la sección crítica
• Sistema compuesto por n procesos
• Cada uno tiene un fragmento de código: sección crítica
• Sólo uno de los procesos en cada instante puede ejecutar en la
sección crítica
– Cuando un proceso está ejecutando en la sección crítica,
ningún otro puede hacerlo
Sistemas operativos: una visión aplicada
9
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo 1
P ro c e s o lig e ro
p rin c ip a l
ni =51
nf = 100
ni = 1
nf = 50
S 2 = 5 1 + ....+ 1 0 0
S 1 = 1 + ...+ 5 0
s u m a _ to ta l = S 1 + S 2
Sistemas operativos: una visión aplicada
10
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo 1
• 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
Sistemas operativos: una visión aplicada
11
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo con sección crítica
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);
}
Sistemas operativos: una visión aplicada
12
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo 2
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
Sistemas operativos: una visión aplicada
13
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo 2 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;
}
Sistemas operativos: una visión aplicada
14
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo 3
P ro c e s a d o r 2
P ro c e s a d o r 1
L e e P ID
P ID = 5 0 0
R e g is tr o o
p o s ic ió n d e m e m o r ia
P ID = 5 0 0
L e e P ID
In c re m e n ta y
a s ig n a P ID
P I D = 5 0 1 E s c r ib e P ID
P ID = 5 0 1
P ID = 5 0 0
In c re m e n ta y
a s ig n a P I D
P ID = 5 0 1
P ID = 5 0 1
Sistemas operativos: una visión aplicada
15
E s c r ib e P ID
© J. Carretero, F. García, P. de Miguel, F. Pérez
Solución al problema de la sección crítica
• Estructura general de cualquier mecanismo utilizado para
resolver el problema de la sección crítica:
Entrada en la sección crítica
Código de la sección crítica
Salida de la sección crítica
• Requisitos que debe ofrecer cualquier solución para resolver el
problema de la sección crítica:
– Exclusión mutua
– Progreso
– Espera limitada
Sistemas operativos: una visión aplicada
16
© J. Carretero, F. García, P. de Miguel, F. Pérez
Problema del productor-consumidor
P ro c e s o
C o n s u m id o r
P ro c e s o
P ro d u c to r
F lu jo d e
d a to s
M e c a n is m o d e
c o m u n ic a c ió n
Sistemas operativos: una visión aplicada
17
© J. Carretero, F. García, P. de Miguel, F. Pérez
El problema de los lectores-escritores
L e c to r
L e c to r
E s c r ito r
L e c to r
E s c r ito r
R e c u rs o
Sistemas operativos: una visión aplicada
18
© J. Carretero, F. García, P. de Miguel, F. Pérez
Comunicación cliente-servidor
C o m p u ta d o r
C o m p u ta d o r
P e t ic ió n
P ro c e s o
c lie n te
P ro c e s o
s e r v id o r
S .O .
R e s p u e s ta
Sistemas operativos: una visión aplicada
19
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
• Procesos concurrentes.
• Problemas clásicos de comunicación y sincronización.
• Mecanismos de comunicación y sincronización.
•
•
•
•
Paso de mensajes.
Aspectos de implementación
Interbloqueos.
Servicios POSIX
Sistemas operativos: una visión aplicada
20
© J. Carretero, F. García, P. de Miguel, F. Pérez
Mecanismos de comunicación
•
•
•
•
Archivos
Tuberías (pipes, FIFOS)
Variables en memoria compartida
Paso de mensajes
Sistemas operativos: una visión aplicada
21
© J. Carretero, F. García, P. de Miguel, F. Pérez
Mecanismos de Sincronización
• Construcciones de los lenguajes concurrentes (procesos ligeros)
• Servicios del sistema operativo:
– Señales (asincronismo)
– Tuberías (pipes, FIFOS)
– Semáforos
– Mutex y variables condicionales
– Paso de mensajes
• Las operaciones de sincronización deben ser atómicas
Sistemas operativos: una visión aplicada
22
© J. Carretero, F. García, P. de Miguel, F. Pérez
Tuberías (POSIX)
• Mecanismo de comunicación y sincronización
– Sin nombre: pipes
– Con nombre: FIFOS
• Sólo puede utilizarse entre los procesos hijos del proceso que
creó el pipe
int pipe(int fildes[2]);
•
•
•
Identificación: dos descriptores de archivo
– Para lectura
– Para escritura
Flujo de datos: unidireccional
Mecanismo con capacidad de almacenamiento
Sistemas operativos: una visión aplicada
23
© J. Carretero, F. García, P. de Miguel, F. Pérez
Comunicación unidireccional con tuberías
w r ite
P ro c e s o
d e U s u a r io
P ro c e s o
d e U s u a r io
re a d
SO
p ip e
F lu jo d e d a to s
Sistemas operativos: una visión aplicada
24
© J. Carretero, F. García, P. de Miguel, F. Pérez
Comunicación bidireccional con tuberías
w r it e
P ro c e s o
d e U s u a r io
w r it e
re a d
P ro c e s o
d e U s u a r io
re a d
SO
p ip e
F lu jo d e d a to s
p ip e
F lu jo d e d a to s
Sistemas operativos: una visión aplicada
25
© J. Carretero, F. García, P. de Miguel, F. Pérez
Tuberías (II)
• 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)
Sistemas operativos: una visión aplicada
26
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con tuberías
void main(void) {
int fildes[2];
char c;
/* pipe para sincronizar */
/* caracter para sincronizar */
pipe(fildes);
write(fildes[1], &c, 1);
/* necesario para entrar en la
seccion critica la primera vez */
/* proceso hijo */
if (fork() == 0) {
for(;;) {
read(fildes[0], &c, 1); /* entrada seccion critica */
< Seccion critica >
write(fildes[1], &c, 1); /* salida seccion critica */
}
Sistemas operativos: una visión aplicada
27
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con tuberías (II)
} else {
/* proceso padre */
for(;;) {
read(fildes[0], &c, 1);
/* entrada seccion critica */
< seccion critica >
write(fildes[1], &c, 1); /* salida seccion critica */
}
}
}
Sistemas operativos: una visión aplicada
28
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con tuberías
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) { /* proceso hijo: productor */
for(;;) {
< producir dato_p, escribe 4 enteros *
write(fildes[1], dato_p, 4*sizeof(int));
}
Sistemas operativos: una visión aplicada
29
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con tuberías (II)
} else
{
/* proceso padre: consumidor */
for(;;) {
read(fildes[0], &dato, sizeof(int));
/* consumir dato, lee un entero */
}
}
}
Proceso
hijo
Sistemas operativos: una visión aplicada
Pipe
30
Proceso
padre
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejecución de mandatos con tuberías
/* programa que ejecuta el mandato ``ls | wc'' */
void main(void)
{
int fd[2];
pid_t pid;
ls
pipe
wc
if (pipe(fd) < 0) {
perror(``pipe'');
exit(-1);
}
pid = fork();
switch(pid) {
case -1:
/* error */
perror(``fork'');
exit(-1);
Sistemas operativos: una visión aplicada
31
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejecución de mandatos con tuberías (II)
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'');
}
}
Sistemas operativos: una visión aplicada
32
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejecución de mandatos con tuberías (III)
•Proceso
•Proceso
•Proceso
•STDIN
•STDOUT
•fd[0]
•fd[1]
•STDOUT
•STDIN
•STDOUT
•fd[0]
•fd[1]
•pipe
•STDOUT
•STDIN
•pipe
•exec
•STDOUT
•Proceso hijo
•STDIN
•pipe
•STDOUT
•wc
•ls
Sistemas operativos: una visión aplicada
•redirección
•Proceso hijo
•Proceso
•STDIN
•pipe
•Proceso hijo
•Proceso
•STDIN
•fork()
•STDIN
•STDOUT
•fd[0]
•fd[1]
•pipe(fd)
•STDIN
•STDOUT
33
© J. Carretero, F. García, P. de Miguel, F. Pérez
Tuberías con nombre en POSIX
(FIFOS)
•
•
•
•
Igual que los pipes
Mecanismo de comunicación y sincronización con nombre
Misma máquina
Servicios
– int mkfifo(char *name, mode_t mode);
• Crea un FIFO con nombre name
– int 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()
Sistemas operativos: una visión aplicada
34
© J. Carretero, F. García, P. de Miguel, F. Pérez
Semáforos
•
•
•
•
Mecanismo de sincronización
Misma máquina
Objeto con un valor entero
Dos operaciones atómicas
– wait
– signal
Sistemas operativos: una visión aplicada
35
© J. Carretero, F. García, P. de Miguel, F. Pérez
Operaciones sobre emáforos
wait(s)
{
}
s = s - 1;
if (s < 0) {
<Bloquear al proceso>
}
signal(s)
{
s = s + 1;
if (s <= 0)
<Desbloquear a un proceso bloqueado por la
operacion wait>
}
}
Sistemas operativos: una visión aplicada
36
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con semáforos
wait(s); /* entrada en la seccion critica */
< seccion critica >
signal(s); /* salida de la seccion critica */
•
El semáforo debe tener valor inicial 1
Valor del
semáforo (s)
P0
P1
P2
1
0
-1
wait(s)
wait(s)
wait(s)
-2
desbloquea
-1
signal(s)
desbloquea
0
signal(s)
Ejecutando código de la sección crítica
1
Sistemas operativos: una visión aplicada
signal(s)
37
Proceso bloqueado en el semáforo
© J. Carretero, F. García, P. de Miguel, F. Pérez
Semáforos POSIX
• int 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 wait sobre un semáforo.
• int sem_post(sem_t *sem);
– Realiza la operación signal sobre un semáforo.
Sistemas operativos: una visión aplicada
38
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con semáforos
(buffer acotado y circular)
Productor
Consumidor
Sistemas operativos: una visión aplicada
39
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con semáforos (II)
#define MAX_BUFFER
#define DATOS_A_PRODUCIR
sem_t elementos;
sem_t huecos;
int buffer[MAX_BUFFER];
void main(void)
{
pthread_t th1, th2;
1024
100000
/* tamanio del buffer */
/* datos a producir */
/* elementos en el buffer */
/* huecos en el buffer */
/* buffer comun */
/* identificadores de threads */
/* inicializar los semaforos */
sem_init(&elementos, 0, 0);
sem_init(&huecos, 0, MAX_BUFFER);
Sistemas operativos: una visión aplicada
40
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con semáforos (III)
/* 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);
exit(0);
}
Sistemas operativos: una visión aplicada
41
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con semáforos (IV)
void Productor(void)
/* codigo del productor */
{
int pos = 0; /* posicion 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 */
buffer[pos] = i;
pos = (pos + 1) % MAX_BUFFER;
sem_post(&elementos); /* un elemento mas */
}
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
42
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con semáforos (V)
void Consumidor(void)
{
int pos = 0;
int dato;
int i;
/* codigo del Consumidor */
for(i=0; i < DATOS_A_PRODUCIR; i++ ) {
sem_wait(&elementos);
/* un elemento menos */
dato = buffer[pos];
pos = (pos + 1) % MAX_BUFFER;
sem_post(&huecos);
/* un hueco mas */
/* cosumir dato */
}
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
43
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con semáforos
int dato = 5;
/* recurso */
int n_lectores = 0; /* numero de lectores */
sem_t sem_lec;
/* controlar el acceso n_lectores */
sem_t mutex;
/* controlar el 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,
pthread_create(&th2,
pthread_create(&th3,
pthread_create(&th4,
Sistemas operativos: una visión aplicada
NULL,
NULL,
NULL,
NULL,
Lector, NULL);
Escritor, NULL);
Lector, NULL);
Escritor, NULL);
44
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con semáforos (II)
pthread_join(th1,
pthread_join(th2,
pthread_join(th3,
pthread_join(th4,
NULL);
NULL);
NULL);
NULL);
/* cerrar todos los semaforos */
sem_destroy(&mutex);
sem_destroy(&sem_lec);
exit(0);
}
Sistemas operativos: una visión aplicada
45
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con semáforos (III)
/* codigo del lector */
void Lector(void) { /* codigo 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);
}
Sistemas operativos: una visión aplicada
46
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con semáforos (IV)
/* código del escritor */
void Escritor(void) {
sem_wait(&mutex);
dato = dato + 2;
sem_post(&mutex);
/* codigo del escritor */
/* modificar el recurso */
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
47
© J. Carretero, F. García, P. de Miguel, F. Pérez
Memoria compartida
P ro c e s o A
P ro c e s o B
T e x to
T e x to
D a to s
v a r2
D a to s
v a r1
2
P ila
S e g m e n to
d e m e m o r ia
c o m p a r tid a
P ila
• Declaración independiente de variables
Sistemas operativos: una visión aplicada
48
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con
memoria compartida
• Productor:
– Crea los semáforos (shm_open)
– Crea la zona de memoria compartida utilizando un archivo proyectado en
memoria (open)
– Le asigna espacio (ftruncate)
– Proyecta el archivo en su espacio de direcciones (mmap)
– Utiliza la zona de memoria compartida
– Desproyecta la zona de memoria compartida
– Cierra y borra el archivo.
• Consumidor:
–
–
–
–
–
Abre los semáforos (shm_open)
Debe esperar a que archivo esté creado para abrirlo (open)
Proyecta el archivo en su espacio de direcciones (mmap)
Utiliza la zona de memoria compartida
Cierra el archivo.
Sistemas operativos: una visión aplicada
49
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del productor
#define MAX_BUFFER
#define DATOS_A_PRODUCIR
sem_t *elementos;
sem_t *huecos;
1024
100000
/* tamaño del buffer */
/* datos a producir */
/* elementos en el buffer */
/* huecos en el buffer */
void main(int argc, char *argv[]){
int shd;
int *buffer;
/* buffer comun */
/* el productor crea el archivo a proyectar */
shd = 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*/
buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int),
PROT_WRITE, MAP_SHARED, shd, 0);
Sistemas operativos: una visión aplicada
50
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del productor (II)
/* El productor crea los semaforos */
elementos = sem_open("ELEMENTOS", O_CREAT, 0700, 0);
huecos = sem_open("HUECOS", O_CREAT, 0700, MAX_BUFFER);
Productor(buffer);
/* desproyectar el buffer compartido */
munmap(buffer, MAX_BUFFER * sizeof(int));
close(shd);
/* cerrar el objeto de memoria compartida */
unlink("BUFFER"); /* borrar el objeto de memoria */
sem_close(elementos);
sem_close(huecos);
sem_unlink("ELEMENTOS");
sem_unlink("HUECOS");
}
Sistemas operativos: una visión aplicada
51
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del productor (III)
void Productor(int *buffer)
/* codigo del productor */
{
int pos = 0; /* posicion 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 */
buffer[pos] = i;
pos = (pos + 1) % MAX_BUFFER;
sem_post(elementos); /* un elemento mas */
}
return;
}
Sistemas operativos: una visión aplicada
52
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del consumidor
#define MAX_BUFFER
#define DATOS_A_PRODUCIR
sem_t *elementos;
sem_t *huecos;
1024
100000
/* tamanio del buffer */
/* datos a producir */
/* elementos en el buffer */
/* huecos en el buffer */
void main(int argc, char *argv[]){
int shd;
int *buffer;
/* buffer comun */
/* el consumidor abre el archivo a proyectar */
shd = open("BUFFER", O_RDONLY);
/*proyectar el objeto de memoria compartida en el espacio de
direcciones del productor*/
buffer = (int *) mmap(NULL, MAX_BUFFER * sizeof(int),
PROT_READ, MAP_SHARED, shd, 0);
Sistemas operativos: una visión aplicada
53
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del consumidor (II)
/* El consumidor abre los semaforos */
elementos = sem_open("ELEMENTOS", 0);
huecos = sem_open("HUECOS", 0);
Consumidor(buffer);
/* desproyectar el buffer compartido */
munmap(buffer, MAX_BUFFER * sizeof(int));
close(shd);
/* cerrar el objeto de memoria compartida */
/* cerrar los semaforos */
sem_close(elementos);
sem_close(huecos);
}
Sistemas operativos: una visión aplicada
54
© J. Carretero, F. García, P. de Miguel, F. Pérez
Código del consumidor (III)
void Consumidor(char *buffer)
{
int pos = 0;
int i, dato;
/* codigo del Consumidor */
for(i=0; i < DATOS_A_PRODUCIR; i++ ) {
sem_wait(elementos);
/* un elemento menos */
dato = buffer[pos];
pos = (pos + 1) % MAX_BUFFER;
sem_post(huecos);
/* un hueco mas */
printf("Consume %d \n", dato); /* cosumir dato */
}
return;
}
Sistemas operativos: una visión aplicada
55
© J. Carretero, F. García, P. de Miguel, F. Pérez
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.
Sistemas operativos: una visión aplicada
56
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con mutex
lock(m);
/* entrada en la seccion critica */
< seccion critica >
unlock(s); /* salida de la seccion critica */
•
La operación unlock debe realizarla el proceso ligero que
ejecutó lock
P ro c e s o
lig e r o A
P ro c e s o
lig e r o B
lo c k m u te x
lo c k m u te x
S e c c ió n
c r ític a
o b tie n e m u te x
u n lo c k m u te x
P r o c e s o lig e r o e je c u t a n d o
P r o c e s o lig e r o b lo q u e a d o
P u n t o d e s in c r o n iz a c ió n
Sistemas operativos: una visión aplicada
57
© J. Carretero, F. García, P. de Miguel, F. Pérez
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
Sistemas operativos: una visión aplicada
58
© J. Carretero, F. García, P. de Miguel, F. Pérez
Variables condicionales (II)
P ro c e s o
lig e r o A
lo c k
w a it
P ro c e s o
lig e r o B
lo c k
A d q u ie r e e l m u te x
u n lo c k m u t e x
s ig n a l
S e c o m p it e p o r e l m u t e x
A d q u ie r e e l m u te x
u n lo c k
P r o c e s o lig e r o b lo q u e a d o e s p e r a n d o u n lo c k
P r o c e s o lig e r o b lo q u e a d o e s p e r a n d o s ig n a l
Sistemas operativos: una visión aplicada
59
© J. Carretero, F. García, P. de Miguel, F. Pérez
Uso de mutex y variables condicionales
• Proceso ligero A
lock(mutex); /* acceso al recurso */
comprobar las estructuras de datos;
while (recurso ocupado)
wait(condition, mutex);
marcar el recurso como ocupado;
unlock(mutex);
• Proceso ligero B
lock(mutex); /* acceso al recurso */
marcar el recurso como libre;
signal(condition, mutex);
unlock(mutex);
• Importante utilizar while
Sistemas operativos: una visión aplicada
60
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servicios POSIX
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.
Sistemas operativos: una visión aplicada
61
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servicios POSIX (II)
int pthread_cond_destroy(pthread_cond_t *cond);
– Destruye un variable condicional.
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.
Sistemas operativos: una visión aplicada
62
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con mutex
#define MAX_BUFFER
#define DATOS_A_PRODUCIR
1024
100000
/* tamanio del buffer */
/* datos a producir */
pthread_mutex_t mutex;
buffer compartido */
pthread_cond_t no_lleno;
pthread_cond_t no_vacio;
int n_elementos;
/* mutex para controlar el acceso al
int buffer[MAX_BUFFER];
/* buffer comun */
/* controla el llenado del buffer */
/* controla el vaciado del buffer */
/* numero de elementos en el buffer */
main(int argc, char *argv[]){
pthread_t th1, th2;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&no_lleno, NULL);
pthread_cond_init(&no_vacio, NULL);
Sistemas operativos: una visión aplicada
63
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con mutex (II)
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);
}
Sistemas operativos: una visión aplicada
64
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con mutex (III)
void Productor(void) {
int dato, i ,pos = 0;
/* codigo del productor */
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); /* se bloquea */
buffer[pos] = i;
pos = (pos + 1) % MAX_BUFFER;
n_elementos ++;
pthread_cond_signal(&no_vacio);
/* buffer no vacio */
pthread_mutex_unlock(&mutex);
}
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
65
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con mutex (IV)
void Consumidor(void) {
/* codigo del sonsumidor */
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 vacio */
pthread_cond_wait(&no_vacio, &mutex); /* se bloquea */
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);
}
Sistemas operativos: una visión aplicada
66
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con mutex
int dato = 5;
int n_lectores = 0;
pthread_mutex_t mutex;
pthread_mutex_t mutex_lectores;
*/
/*
/*
/*
/*
recurso */
numero de lectores */
controlar el acceso a dato */
controla la variable n_lectores
main(int argc, char *argv[]) {
pthread_t th1, th2, th3, th4;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&no_lectores, NULL);
pthread_create(&th1,
pthread_create(&th2,
pthread_create(&th3,
pthread_create(&th4,
Sistemas operativos: una visión aplicada
NULL,
NULL,
NULL,
NULL,
Lector, NULL);
Escritor, NULL);
Lector, NULL);
Escritor, NULL);
67
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con mutex (II)
pthread_join(th1, NULL);
pthread_join(th2, NULL);
pthread_join(th3, NULL);
pthread_join(th4, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&no_lectores);
exit(0);
}
void Escritor(void) {
/* codigo del escritor */
pthread_mutex_lock(&mutex);
dato = dato + 2;
/* modificar el recurso */
pthread_mutex_unlock(&mutex);
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
68
© J. Carretero, F. García, P. de Miguel, F. Pérez
Lectores-escritores con mutex (III)
void Lector(void) { /* codigo 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);
}
Sistemas operativos: una visión aplicada
69
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
• Procesos concurrentes.
• Problemas clásicos de comunicación y sincronización.
• Mecanismos de comunicación y sincronización.
• Paso de mensajes.
• Aspectos de implementación
• Interbloqueos.
• Servicios POSIX
Sistemas operativos: una visión aplicada
70
© J. Carretero, F. García, P. de Miguel, F. Pérez
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(destino, mensaje) recibe un mensaje del proceso destino
• Múltiples soluciones
• Aspectos de diseño
– Tamaño del mensaje
– Flujo de datos (unidireccional, bidireccional)
– Nombrado
• Directo
• Indirecto (puertos, colas)
– Sincronización (síncrono, asíncrono)
– Almacenamiento
Sistemas operativos: una visión aplicada
71
© J. Carretero, F. García, P. de Miguel, F. Pérez
Uso de colas y puertos
P r o c e s o c lie n te
P r o c e s o c lie n te
send
P r o c e s o c lie n t e
r e c e iv e
P r o c e s o c lie n te
send
P u e rto
m e n s a je
m e n s a je
C o la d e m e n s a je s
C o m u n ic a c ió n c o n
c o la s d e m e n s a je s
Sistemas operativos: una visión aplicada
C o m u n ic a c ió n c o n p u e r to s
72
© J. Carretero, F. García, P. de Miguel, F. Pérez
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.
Sistemas operativos: una visión aplicada
73
© J. Carretero, F. García, P. de Miguel, F. Pérez
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 con prioridad prio;
– Recepción bloqueante o no.
Sistemas operativos: una visión aplicada
74
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con colas de mensajes
void main(void)
{
mqd_t mutex;
/* cola de mensajes para sincronizar el
acceso a la seccion critica */
struct mq_attr attr; /* atributos de la cola de mensajes */
char c;
/* caracter para sincronizar */
attr.mq_maxmsg = 1;
attr.mq_msgsize = 1;
/* numero maximo de mensajes */
/* tamanio del mensaje */
mutex = mq_open(``MUTEX'', O_CREAT|O_RDWR, 0777, &attr);
mq_send(mutex, &c, 1, 0);
seccion
/* necesario para entrar en la
critica la primera vez */
Sistemas operativos: una visión aplicada
75
© J. Carretero, F. García, P. de Miguel, F. Pérez
Secciones críticas con colas de mensajes (II)
if (fork() == 0)
{ /* proceso hijo */
for(;;) {
mq_receive(mutex, &c, 1, 0); /* entrada seccion critica */
/* seccion critica */
mq_send(mutex, &c, 1, 0); /* salida seccion critica */
} else {
/* proceso padre */
for(;;) {
mq_receive(mutex, &c, 1, 0); /* entrada seccion critica */
/* seccion critica */
mq_send(mutex, &c, 1, 0); /* salida seccion critica */
}
}
}
Sistemas operativos: una visión aplicada
76
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con colas de
mensajes
#define MAX_BUFFER
1024
/* tamanio del buffer */
#define DATOS_A_PRODUCIR
100000
/* datos a producir */
mqd_t almacen;
/* cola de mensaje donde dejar los datos
producidos
y recoger los datos a consumir */
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);
if (almacen == -1) {
perror("mq_open");
exit(0);
}
Sistemas operativos: una visión aplicada
77
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con colas de
mensajes (II)
if (fork() == 0)
/* proceso hijo */
Productor();
else
/* proceso padre */
Consumidor();
exit(0);
}
void Productor(void)
/* codigo del productor */
{
int dato, i;
for(i=0; i < DATOS_A_PRODUCIR; i++ ) {
dato = i;
/* producir dato */
printf("Produce %d \n", dato);
mq_send(almacen, &dato, sizeof(int), 0);
}
return;
}
Sistemas operativos: una visión aplicada
78
© J. Carretero, F. García, P. de Miguel, F. Pérez
Productor-consumidor con colas de
mensajes (III)
/* codigo del consumidor */
void Consumidor(void)
{
int dato;
int i;
for(i=0; i < DATOS_A_PRODUCIR; i++ ) {
mq_receive(almacen, &dato, sizeof(int), 0);
/* cosumir dato */
printf("Consume
%d \n", dato);
}
return;
}
Sistemas operativos: una visión aplicada
79
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servidor multithread con colas de
mensajes
P r o c e s o c lie n te
C o la d e l
c lie n te
P r o c e s o c lie n te
C o la d e l
c lie n te
p e tic ió n
p e tic ió n
P r o c e s o s e r v id o r
re s p u e s ta
C o la d e l
s e r v id o r
re s p u e s ta
C r e a c ió n d e l
th re a d
C r e a c ió n d e l
th re a d
T h re a d q u e
s ir v e la p e t ic ió n
Sistemas operativos: una visión aplicada
T h re a d q u e
s ir v e la p e tic ió n
80
© J. Carretero, F. García, P. de Miguel, F. Pérez
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 variables condicionales para proteger 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;
struct mensaje mess;
struct mq_attr q_attr;
pthread_attr_t t_attr;
Sistemas operativos: una visión aplicada
/*
/*
/*
/*
cola del servidor */
mensaje a recibir */
atributos de la cola */
atributos de los threads */
81
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servidor multithread con colas de
mensajes (II)
attr.mq_maxmsg = 20;
attr.mq_msgsize = sizeof(struct mensaje));
q_servidor = mq_open("SERVIDOR", O_CREAT|O_RDONLY, 0700, &attr);
pthread_mutex_init(&mutex_mensaje, NULL);
pthread_cond_init(&cond, NULL);
pthread_attr_init(&attr);
pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Sistemas operativos: una visión aplicada
82
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servidor multithread con colas de
mensajes (III)
while (TRUE) {
mq_receive(q_servidor, &mess, sizeof(struct mensaje), 0);
pthread_create(&thid, &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);
}
}
Sistemas operativos: una visión aplicada
83
© J. Carretero, F. García, P. de Miguel, F. Pérez
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 respueta; /* 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*/
pthread_mutex_lock(&mutex_mensaje);
mensaje_no_copiado = FALSE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex_mensaje);
Sistemas operativos: una visión aplicada
84
© J. Carretero, F. García, P. de Miguel, F. Pérez
Servidor multithread con colas de
mensajes (V)
/* ejecutar la petición del cliente */
/* y preparar respuesta */
/* responder al cliente a su cola */
q_cliente = mq_open(mensaje_local.nombre, O_WRONLY);
mqsend(q_cliente, (char *) &respueta, sizeof(respuesta), 0);
mq_clise(q_cliente);
pthread_exit(0);
}
Sistemas operativos: una visión aplicada
85
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo de código cliente
/* estructura de un mensaje */
struct mensaje {
char buffer[1024]; /* datos a enviar */
char cliente[256]; /* cola del cliente */
};
void main(void)
{
mqd_t q_servidor;
mqd_t q_cliente;
struct mq_attr q_attr;
struct mensaje peticion;
struct mensaje respuesta;
/*
/*
/*
/*
/*
cola del servidor */
cola del cliente */
atributos de la cola */
peticion al servidor */
respuesta del servidor */
attr.mq_maxmsg = 1;
attr.mq_msgsize = sizeof(struct mensaje));
Sistemas operativos: una visión aplicada
86
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo de código cliente (II)
q_cliente = mq_open("CLIENTE", O_CREAT|O_RDONLY, 0700, 0);
q_servidor = mq_open("SERVIDOR", O_WRONLY);
/* preparar peticion */
mq_send(q_servidor, &petcicion, sizeof(struct mensaje), 0);
/* esperar respuesta */
mq_receive(q_cliente, &respuesta, sizeof(struct mensaje), 0);
mq_close(q_servidor);
mq_close(q_cliente);
mq_unlink("CLIENTE");
exit(0);
}
Sistemas operativos: una visión aplicada
87
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
•
•
•
•
Procesos concurrentes.
Problemas clásicos de comunicación y sincronización.
Mecanismos de comunicación y sincronización.
Paso de mensajes.
• Aspectos de implementación
• Interbloqueos.
• Servicios POSIX
Sistemas operativos: una visión aplicada
88
© J. Carretero, F. García, P. de Miguel, F. Pérez
Implementación de mecanismos de sincronización
•
Espera activa:
wait(s) {
s = s – 1;
while (s < 0)
;
signal(s) {
s = s + 1;
•
Espera pasiva
wait(s) {
s = s – 1;
if (s < 0)
Bloquear al proceso;
signal(s) {
s = s + 1;
if (s <= 0)
Desbloquear a un proceso bloqueado en la operación wait;
}
Sistemas operativos: una visión aplicada
89
© J. Carretero, F. García, P. de Miguel, F. Pérez
Implementación de la espera pasiva
• Operaciones para bloquear a un proceso en un semáforo
T a b la d e p r o c e s o s
BCP1
E s ta d o
P ID
BCP2
B lo q .
7
0
C o la a s o c ia d a
a l s e m á fo ro
BCP3
BC P4
BCP5
BCP6
BCP7
BCP8
6
1
E je c .
11
5
0
BC P9
B C P 1 0 B C P 11 B C P 1 2
8
9
7
(a )
T a b la d e p r o c e s o s
BCP1
BCP2
0
B lo q .
7
E s ta d o
P ID
C o la a s o c ia d a
a l s e m á fo ro
7
BCP3
BC P4
6
BCP5
BCP6
1
B lo q .
11
BCP7
BCP8
BC P9
B C P 1 0 B C P 11 B C P 1 2
B lo q .
5
0
8
9
11
(b )
Sistemas operativos: una visión aplicada
90
© J. Carretero, F. García, P. de Miguel, F. Pérez
Implementación de la espera pasiva
•
Operaciones para desbloquear a un proceso bloqueado en un semáforo
T a b la d e p r o c e s o s
BC P1
BC P2
0
B lo q .
7
E s ta d o
P ID
C o la a s o c ia d a
a l s e m á fo ro
7
BC P3
BC P4
BC P5
BCP6
BC P7
BC P8
6
1
B lo q .
11
5
0
BC P9
B C P 1 0 B C P 11 B C P 12
B lo q .
8
9
11
(a )
T a b la d e p r o c e s o s
BC P1
BC P2
0
L is to
7
E s ta d o
P ID
C o la a s o c ia d a
a l s e m á fo ro
BC P3
BC P4
BC P5
BCP6
BC P7
BC P8
6
1
B lo q .
11
5
0
BC P9
B C P 1 0 B C P 11 B C P 12
B lo q .
8
9
11
(b )
Sistemas operativos: una visión aplicada
91
© J. Carretero, F. García, P. de Miguel, F. Pérez
Instrucciones hardware especiales
• Operación atómicas
int test-and-set(int *valor) {
int temp;
}
temp = *valor;
*valor = 1;
return temp;
/* true */
void swap (int *a, int *b) {
int temp;
}
temp = *a;
*a = *b;
*b = temp;
return;
Sistemas operativos: una visión aplicada
92
© J. Carretero, F. García, P. de Miguel, F. Pérez
Sección crítica con test-and-set
• Los procesos comparten la variable lock (con valor inicial a
false)
while (test-and-set(&lock))
;
<Código de la sección crítica>
lock = false;
Sistemas operativos: una visión aplicada
93
© J. Carretero, F. García, P. de Miguel, F. Pérez
Sección crítica con swap
• Los procesos comparten la variable lock con valor inicial false.
• Cada proceso utiliza una variable local llave.
llave = true;
do
swap(lock, llave);
while (llave != false);
<Código de la sección crítica>
lock = false;
Sistemas operativos: una visión aplicada
94
© J. Carretero, F. García, P. de Miguel, F. Pérez
Implementación de un semáforo con test-andset
wait(s){
while (test-and-set(&valor_s))
;
s = s - 1;
if (s < 0){
valor_s = false;
Bloquear al proceso;
}
else
valor_s = false;
}
Sistemas operativos: una visión aplicada
signal(s){
while (test-and-set(&valor_s))
;
s = s + 1;
if ( s <= 0){
Desbloquear a un proceso
bloqueado en la operación
wait;
}
valor_s = false;
}
95
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
•
•
•
•
•
Procesos concurrentes.
Problemas clásicos de comunicación y sincronización.
Mecanismos de comunicación y sincronización.
Paso de mensajes.
Aspectos de implementación
• Interbloqueos.
• Servicios POSIX
Sistemas operativos: una visión aplicada
96
© J. Carretero, F. García, P. de Miguel, F. Pérez
Interbloqueos
• Bloqueo permanente de un conjunto de procesos que compiten
por los recursos del sistema o se comunican entre sí.
•
Ejemplo: Si P y Q con semáforos con valor inicial 1
P1
wait(P)
wait(Q)
...
signal(P)
signal(Q)
Sistemas operativos: una visión aplicada
P2
wait(Q)
wait(P)
...
signal(Q)
siangl(P)
97
© J. Carretero, F. García, P. de Miguel, F. Pérez
Ejemplo de interbloqueo
P
A
B
Q
Sistemas operativos: una visión aplicada
98
© J. Carretero, F. García, P. de Miguel, F. Pérez
Interbloqueos II
•
Ejemplo: Si C1 y C2 son dos colas de mensajes:
P1
receive(P2, M)
...
send(P2, M)
•
P2
receive(P1, N)
...
send(P1, N)
Condiciones del interbloqueo:
– Exclusión mutua
– Retención y espera
– No apropiación
– Espera circular
Sistemas operativos: una visión aplicada
99
© J. Carretero, F. García, P. de Miguel, F. Pérez
Contenido
•
•
•
•
•
•
Procesos concurrentes.
Problemas clásicos de comunicación y sincronización.
Mecanismos de comunicación y sincronización.
Paso de mensajes.
Aspectos de implementación
Interbloqueos.
• Servicios POSIX
Sistemas operativos: una visión aplicada
100
© J. Carretero, F. García, P. de Miguel, F. Pérez
Tuberías
• Crear una tubería sin nombre
– int pipe(int fildes[2]);
• Crear una tuberías con nombre
– int mkfifo(char *name, mode_t mode);
• Abrir una tubería con nombre
– int open(char *fifo, int flag);
• Cerrar una tubería
– int close(int fd);
• Borrar una tubería con nombre
– int unlink(char *fifo);
• Leer de una tubería
– int read(fildes[0], buffer, n);
• Escribir en una tubería
– int write(fildes[1], buffer, n);
Sistemas operativos: una visión aplicada
101
© J. Carretero, F. García, P. de Miguel, F. Pérez
Semáforos
• int 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 wait sobre un semáforo.
• int sem_post(sem_t *sem);
– Realiza la operación signal sobre un semáforo.
Sistemas operativos: una visión aplicada
102
© J. Carretero, F. García, P. de Miguel, F. Pérez
Mutex
• 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.
Sistemas operativos: una visión aplicada
103
;
© J. Carretero, F. García, P. de Miguel, F. Pérez
Variables condicionales
• 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 un variable condicional.
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.
Sistemas operativos: una visión aplicada
104
© J. Carretero, F. García, P. de Miguel, F. Pérez
Colas de mensajes
• 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.
Sistemas operativos: una visión aplicada
105
© J. Carretero, F. García, P. de Miguel, F. Pérez
Colas de mensajes (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 con prioridad prio;
– Recepción bloqueante o no.
Sistemas operativos: una visión aplicada
106
© J. Carretero, F. García, P. de Miguel, F. Pérez
Descargar