martes, 27 de noviembre de 2012

1.6.3 Primitivas de comunicación(IPC).

Hay varias primitivas de comunicación entre procesos (IPC abreviadamente). La mayoría de ellas requieren algún tipo de modificación a los programas para poder usarlas, pero hay una que es fácilmente accesible desde la línea de comandos usando la metáfora de ficheros estándar en UNIX y que no requiere modificación alguna a los programas. Se trata de las tuberías. Siguiendo la metáfora, UNIX nos permite manejar tuberías como si fueran ficheros normales.

Primitivas de comunicación
Algunos kernel tienen operaciones específicas ajustadas a la invocación remota. Amoeba, por ejemplo, tiene DoOperation/GetRequest--SendReply.
Es más eficiente que el simple Send-Receive (y más fiable y legible).
Amoeba y otros sistemas tienen también comunicación con grupos o radiado (parcial) (broadcast).
Es importante para tolerancia de fallos, mejora de rendimiento y reconfigurabilidad.
Diversas variantes: como mensajes, como múltiples RPCs, con un sólo valor devuelto, con varios valores devueltos (todos juntos o pidiendo uno a uno), etc.
En la práctica, mecanismos de comunicación de alto nivel tales como RPC/RMI, radiado y notificación de sucesos (parecido a los manejadores de interrupciones), se implementan en middleware y no en el kernel.
Normalmente, sobre un nivel TCP/IP, por razones de transportabilidad, (aunque resulta ``caro").

La IPC ofrece un mecanismo que permite a los procesos cumunicarse y sincronizar sus acciones. La mejor forma de proveer la comunicación entre procesos es mediante un sistema de mensajes.
La función de un sistema de mensaje es permitir a los procesos comunicarse entre sí sin tener que recurrir a variables compartidas.
Un recurso IPC ofrece por los menos 2 operaciones:
enviar (mensaje) (send) y recibir (mesanje) (receive).
Sea P y Q dos procesos que requieren comunicarse deberán enviarse mensajes; para ello debe existir un enlace de comunicación entre ellos. Este enlace puede implementarse de diversas maneras.
Los métodos para implementar lógicamente un enlace y las operaciones de enviar / recibir son:
·         Comunicación directa o indirecta
·         Uso de buffer automático o explícito
·         Envío por copia o envío por referencia
·         Mensajes de tamaño fijo o variables
Comunicación directa:
Aquí cada proceso que desee comunicarse debe nombrar explícitamente el destinatario o el remitente de la comunicación. Este esquema se define las primitivas de la sig. manera:
Enviar(P,mensaje): Enviar un mensaje al proceso P.
Recibir(Q. Mensaje): Recibir un mensaje del proceso Q.
Con las siguientes propiedades:
·         Se establece automáticamente el enlace entre cada par de procesos. Lo procesos sólo necesitan conocer la identidad de otro para la comunicación.
·         Solo hay un enlace entre cada par de procesos.
·         El enlace puede ser unidireccional o bidireccional.
Este esquema exhibe un simetría de direccionamiento; es decir, los procesos tanto emisor como receptor necesitan nombrar al otro para comunicarse.
Otra variante de este esquema es utilizar asimetría de direccionamiento, con la sig. primitivas:
Enviar(P,mensaje): enviar un mensaje al proceso P.
Recibir(Id,mensaje) : recibir un mensaje de cualquier proceso con el que hubo comunicación.
Aquí sólo el emisor nombra al destinatario; el destinatario no ésta obligado a nombrar al emisor.
Comunicación indirecta:
Aquí los mensajes se envían a, y se reciben de, buzones (también llamados PUERTOS). Un buzón puede considerarse en lo abstracto como un objeto en el que los procesos pueden colocar mensajes y del cual se pueden sacar mensajes. Cada buzón tiene una identificación única. Aquí dos proceso se pueden comunicarse sólo si comparten un buzón. Las primitivas se definen como:
Enviar (A,mensaje): enviar un mensaje al buzón A.
Recibir (A,mensaje): recibir un mensaje del buzón A.
Un enlace de comunicación tiene las sig. propiedades:
·         Se establece un enlace entre un par de procesos sólo si tienen un buzón compartido.
·         Un enlace puede estar asociado a más de dos procesos.
·         Entre cada par de procesos en comunicación puede haber varios enlaces distintos, cada uno de los cuales corresponderá a un buzón.
·         Los enlaces pueden ser unidireccionales o bidereccionales.
Hay varias formas de designar el dueño de y los usuarios de un buzón dado. Una posibilidad es permitir que un proceso declare variables de tipo buzón. El proceso que declara un buzón es el dueño de ese buzón. Cualquier otro proceso que conozca el nombre de dicho buzón podrá usarlo.
Por otro lado, un buzón propiedad del S.O tiene existencia propia; es independiente y no está unido a ningún proceso específico. El S.O establece un mecanismo que permite a un proceso:
·         Crear un buzón nuevo
·         Enviar y recibir mensajes a través del buzón
·         Destruir un buzón.

Comunicación entre procesos (IPC)
Los procesos en UNIX no comparten memoria, ni siquiera los padres con sus hijos. Por tanto, hay que establecer algún mecanismo en caso de que se quiera comunicar información entre procesos concurrentes. El sistema operativo UNIX define tres clases de herramientas de comunicación entre procesos (IPC): los semáforos, la memoria compartida y los mensajes.
El tipo de llamadas al sistema para estos IPCs es análogo al de los semáforos: existen sendas funciones shmget y msgget para crear o enlazarse a un segmento de memoria compartida o a una cola de mensajes, respectivamente. Para alterar propiedades de estos IPCs, incluyendo su borrado, están las funciones shmctl y msgctl.
Para enviar o recibir mensajes, se utilizan las funciones msgsnd y msgrcv.
En este apartado se describirán brevemente algunas llamadas al sistema disponibles para el uso de las IPCs dentro de la programación en C.

Semáforos

¿Qué es un semáforo para el UNIX? Formalmente es muy similar a la definición clásica de Dijkstra, en el sentido de que es una variable entera con operaciones atómicas de inicialización, incremento y decremento con bloqueo.
El UNIX define tres operaciones fundamentales sobre semáforos:
* semget Crea o toma el control de un semáforo
* semctl Operaciones de lectura y escritura del estado del semáforo. Destrucción del semáforo
* semop Operaciones de incremento o decremento con bloqueo
Como el lenguaje C no tiene un tipo "semáforo" predefinido, si queremos usar semáforos tenemos que crearlos mediante una llamada al sistema (semget). Esta llamada permite crear un conjunto de semáforos, en lugar de uno solo. Las operaciones se realizan atómicamente sobre todo el conjunto; esto evita interbloqueos y oscuras programaciones en muchos casos, pero para esta práctica es más bien un engorro.
Al crear un semáforo se nos devuelve un número identificador, que va a funcionar casi igual que los identificadores de fichero de las llamadas open, creat, etc. La función semget nos permite además "abrir" un semáforo que ya esté creado. Así, por ejemplo, si un proceso crea un semáforo, otros procesos pueden sincronizarse con aquél (con ciertas restricciones) disponiendo del semáforo con semget.
Para darle un valor inicial a un semáforo, se utiliza la función semctl.
El UNIX no ofrece las funciones clásicas P y V o equivalentes, sino que dispone de una función general llamada semop que permite realizar una gama de operaciones que incluyen las P y V.
semctl también se emplea para destruir un semáforo.

Llamadas al sistema para semáforos

Esta es una descripción resumida de las tres llamadas al sistema para operar con semáforos (semget, semctl y semop). Para una información más completa y fidedigna, diríjanse al manual de llamadas al sistema (sección 2).
Para el correcto uso de todas estas funciones, han de incluir el fichero cabecera <sys/sem.h>
Las tres funciones devuelven -1 si algo ha ido mal y en tal caso la variable errno informa del tipo de error.

Apertura o creación de un semáforo

Sintaxis:
int semget ( key_t key, int nsems, int semflg );
semget devuelve el identificador del semáforo correspondiente a la clave key. Puede ser un semáforo ya existente, o bien semget crea uno nuevo si se da alguno de estos casos:
a) key vale IPC_PRIVATE. Este valor especial obliga a semget a crear un nuevo y único identificador, nunca devuelto por ulteriores llamadas a semget hasta que sea liberado con semctl.
b) key no está asociada a ningún semáforo existente, y se cumple que (semflg & IPC_CREAT) es cierto.
A un semáforo puede accederse siempre que se tengan los permisos adecuados.
Si se crea un nuevo semáforo, el parámetro nsems indica cuántos semáforos contiene el conjunto creado; los 9 bits inferiores de semflg contienen los permisos estilo UNIX de acceso al semáforo (usuario, grupo, otros).
semflg es una máscara que puede contener IPC_CREAT, que ya hemos visto, o IPC_EXCL, que hace crear el semáforo, pero fracasando si ya existía.
Ejemplo:
int semid = semget ( IPC_PRIVATE, 1, IPC_CREAT | 0744 );

Operaciones de control sobre semáforos

Sintaxis:
int semctl ( int semid, int semnum, int cmd... );
Esta es una función compleja (y de interfaz poco elegante) para realizar ciertas operaciones con semáforos. semid es un identificador de semáforo (devuelto previamente por semget) y semnum, el semáforo del conjunto sobre el que quieren trabajar. cmd es la operación aplicada; a continuación puede aparecer un parámetro opcional según la operación definida por cmd.
Las operaciones que les interesan a ustedes son
GETVAL semctl retorna el valor actual del semáforo
SETVAL se modifica el valor del semáforo (un cuarto parámetro entero da el nuevo valor)
IPC_RMID destruye el semáforo
Ejemplos:
valor = semctl (semid,semnum,GETVAL);
semctl (semid,semnum,SETVAL,nuevo_valor);

Operaciones sobre semáforos

Sintaxis:
int semop ( int semid, struct sembuf* sops, unsigned nsops );
Esta función realiza atómicamente un conjunto de operaciones sobre semáforos, pudiendo bloquear al proceso llamador. semid es el identificador del semáforo y sops es un apuntador a un vector de operaciones. nsops indica el número de operaciones solicitadas.
La estructura sembuf tiene estos campos:
        struct sembuf {
         unsigned short sem_num;       // número del semáforo dentro del conjunto
         short         sem_op; // clase de operación
                                      // según sea >0, <0 o ==0
         short         sem_flg;       // modificadores de operación
        };
Cada elemento de sops es una operación sobre algún semáforo del conjunto de semid. El algoritmo simplificado de la operación realizada es éste (semval es el valor entero contenido en el semáforo donde se aplica la operación).
        si semop<0
               si semval >= |semop|
                       semval -= |semop|
               si semval < |semop|
                       si (semflag & IPC_NOWAIT)!=0
                               la función semop() retorna
                       si no
                               bloquearse hasta que semval >= |semop|
                               semval -= |semop|
        si semop>0
               semval += semop
        si semop==0
               si semval = 0
                       SKIP
               si semval != 0
                       si (semflag & IPC_NOWAIT)!=0
                               la función semop() retorna
                       si no
                               bloquearse hasta que semval == 0
Resumiendo un poco, si el campo semop de una operación es positivo, se incrementa el valor del semáforo. Asimismo, si semop es negativo, se decrementa el valor del semáforo si el resultado no es negativo. En caso contrario el proceso espera a que se dé esa circunstancia. Es decir, semop==1 produce una operación V y semop==-1, una operación P.

Ejemplos de uso

Para ilustrar de forma concreta el empleo de semáforos bajo UNIX, les mostramos unos ejemplos de subrutinas en C que les pueden servir como modelos para elaborar sus rutinas de sincronización en las prácticas de la asignatura.
En concreto, son unas funciones que implementan las operaciones P y V de un semáforo clásico (inicialización, incremento y decremento con posible bloqueo del proceso llamador). Así definidas, o con pocas modificaciones, les pueden servir como la interfaz para uso de semáforos en sus aplicaciones.
#include <sys/types.h> /* para key_t */
 
/* Crea un semáforo con un valor inicial, dada una clave */
/* Devuelve el identificador (válido o no) del semáforo */
int crea_sem ( key_t clave, int valor_inicial );
 
/* Operaciones P y V sobre un semáforo */
void sem_P ( int semid );      
void sem_V ( int semid );
 
 
/***********************************/
/********   IMPLEMENTACIÓN   *******/
/***********************************/
        
#include <sys/ipc.h>
#include <sys/sem.h>
#define PERMISOS 0644
 
/* crea_sem: abre o crea un semáforo */
 
int crea_sem ( key_t clave, int valor_inicial )
{
 int semid = semget(           /* Abre o crea un semáforo... */
                       clave,                 /* con una cierta clave */
                       1,                             /* con un solo elemento */
                       IPC_CREAT|PERMISOS     /* lo crea (IPC_CREAT) con 
                                                        unos PERMISOS */
                        ); 
 
 if ( semid==-1 ) return -1;
 
 /* Da el valor inicial al semáforo */
 semctl ( semid, 0, SETVAL, valor_inicial );
 return semid;                                                                       
}
 
/* abre_sem: Abrir un semáforo que otro proceso ya creó */
 
int abre_sem (key_t clave)     
{
 return semget(clave,1,0);
}
 
 
/* Operaciones P y V */
 
void sem_P ( int semid )       /* Operación P */
{
        struct sembuf op_P [] =
        {
          0, -1, 0     /* Decrementa semval o bloquea si cero */
        };
 
        semop ( semid, op_P, 1 );
}
 
void sem_V ( int semid )       /* Operación V */
{
        struct sembuf op_V [] =
        {
           0, 1, 0             /* Incrementa en 1 el semáforo */
        };
        semop ( semid, op_V, 1 );
}



No hay comentarios:

Publicar un comentario