Volver al temario
Tema 04 · 70 páginas

Comunicación y sincronización

Tema completo Modo estudio
TEXTO ORIGINAL · Páginas 5558 Ver PDF

Solucionario

Texto íntegro de la conversión. Las figuras y la disposición de las notas se pueden consultar en el PDF.

Página 55

Solucionario

Ejercicios de autoevaluación

  1. El código propuesto para el generador de números aleatorios es el siguiente:

void Productor() {for (;;)

{n = crearNombre (); sem_wait(full); /Esperar a que pueda poner un elemento/ sem_wait(mutexProd); /Exclusión mutua entre productores/ /(y aleatorizadores)/ buffer[in] = n; in = (in + 1) % maxbuff; sem_signal(mutexProd); /Fin de exclusión/ sem_signal(empty); /Indica nuevo elemento en el vector/ /de memoria intermedia/ } }

int Consumidor () /Toma un número cualquiera del vector/ /de memoria intermedia/ {Return buffer[out]; }

void Aleatorizador () /SOLUCIÓN 1.0/ {/Variables locales/ int i,aux; int n[5]; for (;;){ /FALLA/ for (i = 0; i < 5; i++) sem_wait(empty); /Espera a que haya 5 o más elementos/ sem_wait(mutexAleat); /Exclusión entre aleatorizadores/ for (i = 0; i < 5; i++){ n[i] = buffer [out]; out = (out + 1) % maxbuff; sem_signal(full); /FALLA/ } /Fin de la exclusión. Ha extraído 5/ sem_signal(mutexAleat); /Elementos consecutivos/ aux = procesa (n[0], n[1], n[2], n[3], n[4]); sem_wait(full); /Espera a que haya sitio/ /Exclusión mutua entre productores (y aleatorizadores)/ sem_wait(mutexProd); buffer[in] = aux;in = (in + 1) % maxbuff; sem_signal(mutexProd); /Fin de exclusión/ sem_signal(empty); /Elemento nuevo disponible/ } }

Cabe observar que el aleatorizador puede traer problemas. Por ejemplo, si sólo hay un aleatorizador, mientras está procesando los últimos cinco números que ha extraído, los productores pueden llenar el vector de memoria intermedia –se quedarán todos bloqueados–, el aleatorizador hará un sem_wait(full) –también se bloqueará– y nadie consumirá elementos.

Para arreglar esto, sólo hay que conseguir que el aleatorizador se guarde el sitio del último elemento que extrae: se puede sustituir la línea de código.

sem_signal(full);/FALLA/

para

if (i < 4) sem_signal(full);/NO FALLA/

y eliminar el

sem_wait(full);/Espera a que haya sitio/

Por otra parte, si hay más de un aleatorizador, éstos se pueden repartir los elementos del vector de memoria intermedia sin que ninguno de ellos tome cinco –haga cinco sem_wait(empty)–, el vector de memoria intermedia se puede llenar y se quedaría muy bloqueado porque ningún aleatorizador sacaría elementos.

Página 56

Para corregir estos dos problemas, proponemos la solución siguiente para el aleatorizador:

void Aleatorizador () /SOLUCIÓN 1.1/ {int i,aux; /Declaración de variables locales/ int n[5]; for (;;) {sem_wait(mutexAleat); /Exclusión mutua entre aleatorizadores./ /Evita que algún otro aleatorizador pueda/ /quitarle elementos/ for (i = 0; i < 5; i++) {Sem_wait(empty); /Espera un elemento/ n[i] = buffer[out]; out = (out + 1) % maxbuff; if (i < 4) sem_signal(full); /NO FALLA/ } /Fin de la exclusión. Ha extraído 5/ sem_signal(mutexAleat); /Elementos consecutivos/ aux = procesa (n[0], n[1], n[2], n[3], n[4]); /En este punto, SEGURO QUE TENEMOS SITIO PARA UN ELEMENTO/ sem_wait(mutexProd); /Exclusión mutua entre/ /Productores (y Aleatorizadores)/ buffer[in] = aux; in = (in + 1) % maxbuff; sem_signal(mutexProd); /Fin de exclusión/ sem_signal(empty); /Elemento nuevo disponible/ } }

  1. Tendremos las variables y las estructuras de datos siguientes en la memoria compartida (además de las necesarias para implementar el vector de memoria intermedia circular):

/Variables que indican si hay algún productor o algún/ /consumidor que actualmente accede al vector de memoria/ /intermedia. Inicialmente no hay ninguno./ int cap_prod_accediendo = 1; int cap_cons_accediendo = 1;

/Vectores que indicarán el número de productores y de consumidores/ /que están bloqueados esperando acceder al vector de memoria/ /intermedia. Todas las posiciones deben estar inicializadas en 0./ int prod_esperando[P]; int cons_esperando[P];

/Vectores de semáforos en los que estarán bloqueados por prioridades/ /los consumidores y los productores. Los semáforos han de estar/ /inicializados en 0. Tendremos 2 semáforos para cada prioridad./ semaphore prod_semaforos[P]; semaphore cons_semaforos[P];

/Semáforos de exclusión mutua productores/consumidores,/ /inicializados en 1./ semaphore prod_mutex, cons_mutex;

/Semáforos de bloqueo en caso de vector de memoria/ /intermedia lleno o vacío/ semaphore full; /Inicializado en N/ semaphore empty; /Inicializado en 0/

/Productores/ elem = producir(); sem_wait(prod_mutex); if (cap_prod_accediendo) { cap_prod_accediendo = 0; sem_signal(prod_mutex); } else { prod_esperando[prioridad()]++; sem_signal(prod_mutex); sem_wait(prod_semaforos[prioridad()]); } sem_wait(full); buffer[in] = elem; /No es necesaria la exclusión porque sólo/ in = (in + 1)% N; /Puede acceder 1 productor/

Página 57

sem_signal(empty); sem_wait(prod_mutex); precio (i = P - 1; (i >= 0) && (prods_esperando[i] == 0);i--); if (i < 0) cap_prod_accediendo = 1; else { prod_esperando[i]-; sem_signal(prod_semaforos[i]); } sem_signal(prod_mutex);

/Consumidores/ sem_wait(cons_mutex); if (cap_cons_accediendo) { cap_cons_accediendo = 0; sem_signal(cons_mutex); } else { cons_esperando[prioridad()]++; sem_signal(cons_mutex); sem_wait(cons_semaforos[prioridad()]); } sem_wait(empty); elem = buffer[out]; out = (out + 1) % N; sem_signal(full); sem_wait(cons_mutex); for (i = P - 1;(i >= 0) && (cons_esperando[i] == 0);i--); if (i<0) cap_cons_accediendo = 1; else { cons_esperando[i]-; sem_signal(cons_semaforos[i]); } sem_signal(cons_mutex); consumir(elem);

3.a) Veamos el caso de cada semáforo:

  • El sem1 se debe inicializar en 0. Este semáforo sirve para sincronizar al horchatero y al camarero. Cada vez que el horchatero haya acabado de preparar una horchata, hará un sem_signal sobre sem1 y el camarero la consumirá efectuando un sem_wait.
  • El sem2 se debe inicializar en 0. Este semáforo sincroniza al camarero con el cliente; cuando el cliente haya escrito su petición, hará un sem_signal sobre este semáforo para indicar al camarero que la sirva.
  • El sem3 se debe inicializar en 0. Este semáforo sincroniza al cliente con el camarero. Cuando todas las horchatas que ha pedido el cliente estén servidas, el camarero hará un sem_signal sobre sem3 y el cliente podrá continuar ejecutándose.
  • El sem4 se debe inicializar en 1. Este semáforo regula un acceso en exclusión mutua (garantiza que no pueda haber diferentes clientes realizando peticiones al camarero simultáneamente).
  • El sem5 se debe inicializar en 1. Garantiza el acceso a la variable cuantas_horchatas en exclusión mutua.
  • El sem6 se debe inicializar en 1 porque también es un semáforo de exclusión mutua. Regula el acceso a la variable preparadas.

b) Los semáforos sem5 y sem6 sirven para implementar exclusiones mutuas.

El sem5 no es necesario. Gracias al sem2 y al sem4 no se puede dar el caso de que en el mismo momento diferentes procesos accedan a n_horchatas.

El semáforo sem6 tampoco es necesario, ya que controla una exclusión mutua sobre una variable (preparadas) que no es compartida.

c) El contador de sem1 nos indica el número de horchatas preparadas y pendientes de ser servidas.

El horchatero lo incrementa cada vez que tiene una preparada y el camarero la decrementa cuando consume una.

Página 58

d) El sem4 sirve para tener exclusión mutua entre los clientes. Hasta que uno no está totalmente servido, los otros pueden indicar al camarero que se esperan.

  1. Provocará que el proceso padre se bloquee indefinidamente en el segundo wait porque estará esperando la muerte de uno de sus hijos (el que ejecuta el comando grep), pero éste tampoco morirá porque estará bloqueado leyendo de una pipe vacía en la que existe un proceso escritor, el propio proceso padre. Por lo tanto, tenemos un deadlock en el que el padre está esperando que el hijo muera y el hijo está esperando que el padre cierre el canal de escritura sobre la pipe.

  2. Las señales bloqueadas en cada punto son:

  • A: ninguna
  • B: usr2, pipe
  • C: usr2, pipe, usr1, alrm
  • D: usr2, pipe, usr1, alrm, int
  • E: vacío
  • F: alrm
  • G: alrm
  • H: todos
  • I: alrm

sigprocmask(SIG_BLOCK, NULL, &m); sigaddset(&m, SIGUSR1); sigaddset(&m, SIGUSR2); sigprocmask(SIG_BLOCK, &m, &old); sigdelset(&m, SIGUSR1); sigdelset(&m, SIGUSR2); while ((usr1rebut || usr2rebut) == FALSE) sigsuspend(&m); sigprocmask(SIG_SETMASK, &old, NULL);

sigprocmask(SIG_BLOCK, NULL, &m); sigaddset(&m, SIGUSR1); sigaddset(&m, SIGUSR2); sigprocmask(SIG_BLOCK, &m, &old); sigdelset(&m, SIGUSR1); sigdelset(&m, SIGUSR2); while ((usr1rebut && usr2rebut) == FALSE) sigsuspend(&m); sigprocmask(SIG_SETMASK, &old, NULL);

Ver fragmento extraído sin normalizar
## Página 55

<!-- source-page: 55 -->

GNUFDL • PID_00214803                                                                                  55                                                                                         Comunicación y sincronización

Solucionario

Ejercicios de autoevaluación

1. El código propuesto para el generador de números aleatorios es el siguiente:

void Productor()
{for (;;)

 {n = crearNombre ();
  sem_wait(full); /*Esperar a que pueda poner un elemento*/
  sem_wait(mutexProd); /*Exclusión mutua entre productores*/
  /*(y aleatorizadores)*/
  buffer[in] = n;
  in = (in + 1) % maxbuff;
  sem_signal(mutexProd); /*Fin de exclusión*/
  sem_signal(empty); /*Indica nuevo elemento en el vector*/
  /*de memoria intermedia*/
 }
}

int Consumidor () /*Toma un número cualquiera del vector*/
/*de memoria intermedia*/
{Return buffer[out]; }

void Aleatorizador () /*SOLUCIÓN 1.0*/
{/*Variables locales*/
 int i,aux;
 int n[5];
  for (;;){ /*FALLA*/
   for (i = 0; i < 5; i++)
   sem_wait(empty); /*Espera a que haya 5 o más elementos*/
   sem_wait(mutexAleat); /*Exclusión entre aleatorizadores*/
  for (i = 0; i < 5; i++){
   n[i] = buffer [out];
   out = (out + 1) % maxbuff;
   sem_signal(full); /*FALLA*/
  } /*Fin de la exclusión. Ha extraído 5*/
  sem_signal(mutexAleat); /*Elementos consecutivos*/
  aux = procesa (n[0], n[1], n[2], n[3], n[4]);
  sem_wait(full); /*Espera a que haya sitio*/
  /*Exclusión mutua entre productores (y aleatorizadores)*/
  sem_wait(mutexProd); buffer[in] = aux;in = (in + 1) % maxbuff;
  sem_signal(mutexProd); /*Fin de exclusión*/
  sem_signal(empty); /*Elemento nuevo disponible*/
 }
}

Cabe observar que el aleatorizador puede traer problemas. Por ejemplo, si sólo hay un aleato-
rizador, mientras está procesando los últimos cinco números que ha extraído, los producto-
res pueden llenar el vector de memoria intermedia –se quedarán todos bloqueados–, el alea-
torizador hará un sem_wait(full) –también se bloqueará– y nadie consumirá elementos.

Para arreglar esto, sólo hay que conseguir que el aleatorizador se guarde el sitio del último
elemento que extrae: se puede sustituir la línea de código.

sem_signal(full);/*FALLA*/

para

if (i < 4) sem_signal(full);/*NO FALLA*/

y eliminar el

sem_wait(full);/*Espera a que haya sitio*/

Por otra parte, si hay más de un aleatorizador, éstos se pueden repartir los elementos
del vector de memoria intermedia sin que ninguno de ellos tome cinco –haga cinco
sem_wait(empty)–, el vector de memoria intermedia se puede llenar y se quedaría muy
bloqueado porque ningún aleatorizador sacaría elementos.

## Página 56

<!-- source-page: 56 -->

GNUFDL • PID_00214803                                                                                  56                                                                                         Comunicación y sincronización

Para corregir estos dos problemas, proponemos la solución siguiente para el aleatorizador:

void Aleatorizador () /*SOLUCIÓN 1.1*/
{int i,aux; /*Declaración de variables locales*/
 int n[5];
 for (;;)
 {sem_wait(mutexAleat); /*Exclusión mutua entre aleatorizadores.*/
  /*Evita que algún otro aleatorizador pueda*/
  /*quitarle elementos*/
  for (i = 0; i < 5; i++)
  {Sem_wait(empty); /*Espera un elemento*/
   n[i] = buffer[out];
   out = (out + 1) % maxbuff;
   if (i < 4) sem_signal(full); /*NO FALLA*/
  } /*Fin de la exclusión. Ha extraído 5*/
  sem_signal(mutexAleat); /*Elementos consecutivos*/
  aux = procesa (n[0], n[1], n[2], n[3], n[4]);
  /*En este punto, SEGURO QUE TENEMOS SITIO PARA UN ELEMENTO*/
  sem_wait(mutexProd); /*Exclusión mutua entre*/
  /*Productores (y Aleatorizadores)*/
  buffer[in] = aux;
  in = (in + 1) % maxbuff;
  sem_signal(mutexProd); /*Fin de exclusión*/
  sem_signal(empty); /*Elemento nuevo disponible*/
 }
}

2. Tendremos las variables y las estructuras de datos siguientes en la memoria compartida
(además de las necesarias para implementar el vector de memoria intermedia circular):

/*Variables que indican si hay algún productor o algún*/
/*consumidor que actualmente accede al vector de memoria*/
/*intermedia. Inicialmente no hay ninguno.*/
int cap_prod_accediendo = 1;
int cap_cons_accediendo = 1;

/*Vectores que indicarán el número de productores y de consumidores*/
/*que están bloqueados esperando acceder al vector de memoria*/
/*intermedia. Todas las posiciones deben estar inicializadas en 0.*/
int prod_esperando[P];
int cons_esperando[P];

/*Vectores de semáforos en los que estarán bloqueados por prioridades*/
/*los consumidores y los productores. Los semáforos han de estar*/
/*inicializados en 0. Tendremos 2 semáforos para cada prioridad.*/
semaphore prod_semaforos[P];
semaphore cons_semaforos[P];

/*Semáforos de exclusión mutua productores/consumidores,*/
/*inicializados en 1.*/
semaphore prod_mutex, cons_mutex;

/*Semáforos de bloqueo en caso de vector de memoria*/
/*intermedia lleno o vacío*/
semaphore full; /*Inicializado en N*/
semaphore empty; /*Inicializado en 0*/

/*Productores*/
elem = producir();
sem_wait(prod_mutex);
if (cap_prod_accediendo)
{ cap_prod_accediendo = 0;
  sem_signal(prod_mutex);
}
else
{ prod_esperando[prioridad()]++;
  sem_signal(prod_mutex);
  sem_wait(prod_semaforos[prioridad()]);
}
sem_wait(full);
buffer[in] = elem; /*No es necesaria la exclusión porque sólo*/
in = (in + 1)% N; /*Puede acceder 1 productor*/

## Página 57

<!-- source-page: 57 -->

GNUFDL • PID_00214803                                                                                  57                                                                                         Comunicación y sincronización

sem_signal(empty);
sem_wait(prod_mutex);
precio (i = P - 1; (i >= 0) && (prods_esperando[i] == 0);i--);
if (i < 0)
  cap_prod_accediendo = 1;
else
{ prod_esperando[i]-;
  sem_signal(prod_semaforos[i]);
}
sem_signal(prod_mutex);

/*Consumidores*/
sem_wait(cons_mutex);
if (cap_cons_accediendo)
{ cap_cons_accediendo = 0;
  sem_signal(cons_mutex);
}
else
{ cons_esperando[prioridad()]++;
  sem_signal(cons_mutex);
  sem_wait(cons_semaforos[prioridad()]);
}
sem_wait(empty);
elem = buffer[out];
out = (out + 1) % N;
sem_signal(full);
sem_wait(cons_mutex);
for (i = P - 1;(i >= 0) && (cons_esperando[i] == 0);i--);
if (i<0)
  cap_cons_accediendo = 1;
else
{ cons_esperando[i]-;
  sem_signal(cons_semaforos[i]);
}
sem_signal(cons_mutex);
consumir(elem);

3.a) Veamos el caso de cada semáforo:

•    El sem1 se debe inicializar en 0. Este semáforo sirve para sincronizar al horchatero y al
    camarero. Cada vez que el horchatero haya acabado de preparar una horchata, hará un
    sem_signal sobre sem1 y el camarero la consumirá efectuando un sem_wait.
•    El sem2 se debe inicializar en 0. Este semáforo sincroniza al camarero con el cliente;
    cuando el cliente haya escrito su petición, hará un sem_signal sobre este semáforo para
    indicar al camarero que la sirva.
•    El sem3 se debe inicializar en 0. Este semáforo sincroniza al cliente con el camarero.
    Cuando todas las horchatas que ha pedido el cliente estén servidas, el camarero hará un
    sem_signal sobre sem3 y el cliente podrá continuar ejecutándose.
•    El sem4 se debe inicializar en 1. Este semáforo regula un acceso en exclusión mutua (ga-
    rantiza que no pueda haber diferentes clientes realizando peticiones al camarero simul-
    táneamente).
•    El sem5 se debe inicializar en 1. Garantiza el acceso a la variable cuantas_horchatas en
    exclusión mutua.
•    El sem6 se debe inicializar en 1 porque también es un semáforo de exclusión mutua.
    Regula el acceso a la variable preparadas.

b) Los semáforos sem5 y sem6 sirven para implementar exclusiones mutuas.

El sem5 no es necesario. Gracias al sem2 y al sem4 no se puede dar el caso de que en el mismo
momento diferentes procesos accedan a n_horchatas.

El semáforo sem6 tampoco es necesario, ya que controla una exclusión mutua sobre una
variable (preparadas) que no es compartida.

c) El contador de sem1 nos indica el número de horchatas preparadas y pendientes de ser
servidas.

El horchatero lo incrementa cada vez que tiene una preparada y el camarero la decrementa
cuando consume una.

## Página 58

<!-- source-page: 58 -->

GNUFDL • PID_00214803                                                                                  58                                                                                         Comunicación y sincronización

d) El sem4 sirve para tener exclusión mutua entre los clientes. Hasta que uno no está total-
mente servido, los otros pueden indicar al camarero que se esperan.

4. Provocará que el proceso padre se bloquee indefinidamente en el segundo wait porque
estará esperando la muerte de uno de sus hijos (el que ejecuta el comando grep), pero éste
tampoco morirá porque estará bloqueado leyendo de una pipe vacía en la que existe un pro-
ceso escritor, el propio proceso padre. Por lo tanto, tenemos un deadlock en el que el padre
está esperando que el hijo muera y el hijo está esperando que el padre cierre el canal de
escritura sobre la pipe.

5. Las señales bloqueadas en cada punto son:

•    A: ninguna
•    B: usr2, pipe
•    C: usr2, pipe, usr1, alrm
•    D: usr2, pipe, usr1, alrm, int
•    E: vacío
•    F: alrm
•    G: alrm
•    H: todos
•    I: alrm

6.

sigprocmask(SIG_BLOCK, NULL, &m);
sigaddset(&m, SIGUSR1); sigaddset(&m, SIGUSR2);
sigprocmask(SIG_BLOCK, &m, &old);
sigdelset(&m, SIGUSR1); sigdelset(&m, SIGUSR2);
while ((usr1rebut || usr2rebut) == FALSE)
  sigsuspend(&m);
sigprocmask(SIG_SETMASK, &old, NULL);

7.

sigprocmask(SIG_BLOCK, NULL, &m);
sigaddset(&m, SIGUSR1); sigaddset(&m, SIGUSR2);
sigprocmask(SIG_BLOCK, &m, &old);
sigdelset(&m, SIGUSR1); sigdelset(&m, SIGUSR2);
while ((usr1rebut && usr2rebut) == FALSE)
  sigsuspend(&m);
sigprocmask(SIG_SETMASK, &old, NULL);

Descargar Markdown originalEstudiar este apartado