PRACTICA DE RENDEZ-VOUS.
SISTEMAS OPERATIVOS
Esta página web contiene un trabajo sobre el problema de comunicación estre procesos denominado Rendez-Vous.
Veamos los distintos puntos a tratar.
- Introducción al mecanismo de comunicación entre procesos: Rendez-Vous.
- Ejemplo.
- Documentación del programa.
- Implementación en C.
- Salida.
AUTORAS: - Maria Remedios Carreño García.
-Beatriz López Barrientos.
INTRODUCCION AL MECANISMO DE COMUNICACION ENTRE PROCESOS: RENDEZ-VOUS.
Es el principal mecanismo que se utiliza para el paso de mensajes entre procesos en ADA, cuyas características son:
- Usa primitivas de concurrencia.
- Tiene una sintaxis estricta.
- Tiene un desarrollo modular de programas.
- Utiliza operaciones de bajo nivel.
Los procesos que deseen realizar un encuentro deben de estar activos. El encuentro solo permite una transacción y solo se realiza si tanto el proceso que espera como el que llega "están deacuerdo", es decir, los dos están preparados para la sincronización. Ya que el primer proceso que llega debe esperar a que llegue el segundo que es el momento en el que se produce la sincronización y el intercambio de datos. Después del encuentro cada proceso va a seguir realizando sus tareas independientemente.
En ADA existen varias sentencias para conseguir el encuentro, pero el que hemos implementado en C es el mecanismo de entrada-aceptación ("entry-accept"). La sintaxis y semántica es similar a una llamada a una función. Hay que tener en cuenta que los procesos involucrados poseen una secuencia de sentencias para ser ejecutadas durante el encuentro. Tales sentencias se encuentran en una construcción, Aceptación que puede no ejecutar sentencias si solo se requiere la sincronización. Para que los procesos se reúnan con el poseedor de la sentencia Accept, le dá un nombre único que se hace publico poniendo una sentencia Entry correspondiente en la parte pública de la declaración de ese proceso.
Características del mecanismo de Rendez-Vous:
- Solo permite un enlace y solo uno por cada par de procesos, es decir, es una comunicación entre procesos directa.
- Su capacidad es 0, es decir, el emisor tiene que estar en situación de emisión, y el receptor en situación de "escuchar".
- Es simétrico, es decir, el que llega antes debe esperar al otro.
- Es simple , es decir, en cada encuentro solo se permite realizar una transacción.
- Tiene exclusión mutua entre procesos, es decir, dos procesos en un encuentro, uno que sirve y otro que recibe.
- Es de doble sentido, es decir, la información se intercambia de un proceso a otro y viceversa.
En la implementación de la sentencia Accept de ADA, se puede optar por utilizar:
- Semáforos.
- Exclusión mútua
para resolver el problema que surge a la hora de sincronizar nuestros procesos.
A continuación vamos a detallar un ejemplo para un mejor entendimiento de todo lo explicado anteriormente.
EJEMPLO
Sea un almacén que abastece unos determinados artículos a un tienda asociada a él. Hay que tener en cuenta que el padre del dueño de la tienda es primo hermano del dueño del almacén por lo que esta tienda tiene más prioridad y en el mismo momento que la tienda tiene escasez de unidades el almacén se las manda inmediatamente hasta que el almacén se queda sin existencias.
Para resolver este ejemplo en donde, obviamente, para poder realizar el intercambio de artículos se necesita de un encuentro entre la tienda y el almacén, es decir, se necesitan sincronizar, ya que el almacén tiene que estar dispuesto en cualquier momento para mandar las unidades necesarias que lo hará en el instante en el que la tienda se las pida.
La resolución de este problema va a ser implementada en C. A continuación detallamos la documentación del programa seguido del programa y del correspondiente fichero de salida.
DOCUMENTACION DEL PROGRAMA.
/***********************************************************************************
Programa que simula la sentencia Accept de ADA. Para compilar:
gcc -o ejecuta1 stock.c rshmem.c semaph.c
ejecuta1 es el ejecutable del programa, para ejecutarle:
ejecuta1 numero numero2
numero hace referencia al número de unidades de Inventario de la Tienda.
numero2 hace referencia al número de unidades de Inventario del Almacén
Ver argumentos de la función principal.
**********************************************************************************/
/*********************************************************************************
NOMBRE: Almacen.
SINOPSIS: void Almacen (int *IoAlm, int SemP, int SemH, int IoT)
ARGUMENTOS:
int *IoAlm ==>inventario inicial del Almacén. Puntero a memoria compartida.
int SemP ==>semáforo del proceso Almacén (Padre).
int SemH ==>semáforo del proceso Tienda (Hijo).
int IoT ==>inventario inicial de la Tienda.
DESCRIPCIÓN: Proceso padre
Para la sincronización, utilizamos dos semáforos, que este proceso debe abrir y cerrar:
1. Cierra su propio semáforo (se "duerme" hasta que llegue el proceso hijo).
2. Abre el semáforo del hijo ( le despierta para avisarle de que ya accedió a memoria), para que éste pueda seguir su camino. Esta función se ejecutará hasta que el Inventario del Almacén sea nulo.
**********************************************************************************/
/**********************************************************************************
NOMBRE: Tienda.
SINOPSIS: void Tienda (int *IoAlm, int SemP, int SemH, int IoT)
ARGUMENTOS:
int *IoAlm ==>inventario inicial del Almacén. Puntero a memoria compartida.
int SemP ==>semáforo del proceso Almacen (Padre).
int SemH ==>semáforo del proceso Tienda (Hijo).
int IoT ==>inventario inicial de la tienda.
DESCRIPCION: Proceso hijo. Para conseguir que todo funcione bien este proceso manipula los semáforos de la siguiente manera:
1. Abre el semáforo del proceso Almacen (le "despierta" para avisarle que él ha llegado al "encuentro".
2. Cierra su propio semáforo, esperando a que, en la siguiente iteración el proceso padre le "despierte".
Esta función se ejecutará hasta que Inventario inicial del Almacén sea nulo.
************************************************************************************/
/***********************************************************************************
NOMBRE: main.
SIPNOSIS: void main (int argn, char *argv[])
ARGUMENTOS:
int argn ==> número de argumentos.
char *argv[] ==> argv[0]= nombre del programa.
argv[1]= número de unidades del inventario inicial de la tienda.
argv[2]= número de unidades del inventario inicial del almacén.
DESCRIPCION: función principal Se encarga de :
· Obtener las claves para los recursos ipc de cada semáforo: claveSemP, claveSemH. En caso de error, el programa termina, mostrando por pantalla un mensaje de advertencia.
· Crear los semáforos con comprobación de los posibles errores y terminación del programa si fuese necesario.
· Crear la memoria compartida y en caso de error terminar el programa, cerrando antes los dos semáforos.
· Simular el "encuentro" entre los procesos utilizando la función fork() que realiza una copia del programa a partir de ella, a la que llamaremos proceso hijo. Sincronizamos así el encuentro entre los procesos.
NOTA: Utilizamos como librerías las funciones "rshmem.h" y <sys/sem.h>.
*********************************************************************/
IMPLEMENTACION EN C
#include "rshmem.h"
#include <sys/sem.h>
#define MIN 3
{
int n;
do {
semWait(SemP);
n=Iot-MIN;
if((*IoAlm-n)>0)
{
printf("El almacen envía %d unidades a la tienda\n",n);
fflush(stdout);
}else{
printf("El almacen sólo puede enviar %d unidades\n",(*IoAlm));
fflush(stdout);
}
*IoAlm=*IoAlm-n;
semSignal(SemH);
} while(*IoAlm>0);
}
{
int i;
do{
for(i=IoT; i<=MIN; i--) ;
printf("La tienda tiene escasez de unidades\n");
fflush(stdout);
semSignal(SemP);
semWait(SemH);
}while(*IoAlm>0);
}
{
int *IoAlm;
char *Fin;
int IoT;
int SemP; /* semaforo para el proceso almacen*/
int SemH; /* semaforo para el proceso tienda */
key_t claveSemP;
key_t claveSemH;
IoAlm=(int *)malloc(sizeof(int));
if(argn!=3){
printf("Error en el paso de argumentos del main \n");
exit(-1);
}
IoT=atoi(argv[1]);
/* OBTENER UNA CLAVE CUALQUIERA PARA EL RECURSO IPC */
if((key_t)-1==(claveSemP=ftok("stock.c",'P')))
{
fprintf(stderr, "main: error al crear la clave semáforo con ftok()\n");
exit(1);
}
if((key_t)-1==(claveSemH=ftok("stock.c",'H')))
{
fprintf(stderr, "main: error al crear la clave semáforo con ftok()\n");
exit(1);
}
/* CREAR EL SEMAFORO*/
if(1==(SemP=semCreate(claveSemP,0)))
{
fprintf(stderr, "main: No puede crear semáforo\n");
exit(1);
}
if(1==(SemH=semCreate(claveSemH,0)))
{
fprintf(stderr, "main: No puede crear semáforo\n");
semClose(SemP);
exit(1);
}
/*CREAR MEMORIA COMPARTIDA*/
if(!crearMemoria())
{
fprintf(stderr,"error de crear Memoria\n");
semClose(SemH);
semClose(SemP);
exit(1);
}
IoAlm=(int*)memoria;
*IoAlm=atoi(argv[2]);
Fin=memoria+sizeof(int);
*Fin='a';
if(0!=fork())
/* PROCESO PADRE */
{
Almacen(IoAlm, SemP, SemH, IoT);
semClose(SemP);
semClose(SemH);
while(*Fin!='b'); /*espera la finalizacion del proceso hijo*/
if(!eliminarMemoria()) fprintf(stderr,"Error en eliminar memoria\n");
exit(0);
}else{
/* PROCESO HIJO */
if(-1==(SemP=semOpen(claveSemP)))
fprintf(stderr, "No tengo cualificador de SemP \n");
if(-1==(SemH=semOpen(claveSemH)))
fprintf(stderr, "No tengo cualificador de SemH \n");
Tienda(IoAlm, SemP, SemH, IoT);
semClose(SemP);
semClose(SemH);
*Fin='b'; /*indica el fin del proceso hijo*/
printf("El almacén se ha quedado sin existencias \n");
fflush(stdout);
exit(0);
}
}
SALIDA DEL PROGRAMA
$ ejecuta1 9 15
------ semSignal(1097) -------
------ semWait(1098) -------
------ semWait(1097) -------
------ semSignal(1098) -------
------ semSignal(1097) -------
------ semWait(1098) -------
------ semWait(1097) -------
------ semSignal(1098) -------
------ semSignal(1097) -------
------ semWait(1098) -------
------ semWait(1097) -------
------ semSignal(1098) -------
$