Comunicación entre Procesos
Funciones para IPC en System V

Estas son colas de mensajes, memoria compartida y semáforos.

Estos mecanismos se pueden aplicar a procesos que no están vinculados, pero residen en la misma máquina.

Cada uno de estos mecanismos de comunicación son referidos via un identificador entero no negativo, el cual toma el lugar del archivo en las FIFOs por ejemplo.

El identificador es creado cuando la estructura par IPC es creada. Para ello, se debe proveer de una clave a partir de la cual se crea el identificador.

Esta clave (key) debe ser única dentro de la máquina. Si no es así, la estructura para la comunicación entre procesos no es creada (arroja un error).

La clave (key) puede ser obtenida de tres formas:

  • El servidor crea una nueva estructura espeficicando una clave de IPC_PRIVATE. El procedimiento creador retorna un identificador para la nueva estructura. El problema es que ésta debe ser comunicada al proceso cliente de alguna manera. Por ejemplo, a través de un archivo.
  • Otra forma es que el servidor y cliente se pongan de acuerdo en una clave. El problema es que ésta puede coincidir con otra ya existente.
  • El servidor y el cliente pueden convenir un path y un identificador de proyecto ( 0<= project ID <=255) y llamar a la función ftok, la cual convierte estos dos valores en una clave.

  •         #include <sys/types.h>
            #include <sys/ipc.h>
            key_t ftok(const char * path, int projectID);
    Para crear una nueva estructura IPC, el servidor llama a una de las funciones "get". Ésta requiere una key (IPC_PRIVATE, un valor fijo, o el retornado por ftok) y en el argumento flag se debe activar el bit IPC_CREATE. Activando el bit IPC_EXCL se puede asegurar que la estructura es nueva (de no serlo se retorna un error).

    El cliente accede a la estructura creada por el servidor llamando a la función get con la misma clave y el bit del flag IPC_CREATE desactivado.

    Algunas estructuras de datos son creadas globalmente en el sistema, el cual no maneja un contador de referencias. La estructura permanece en el sistema hasta que es removida por un proceso o la máquina es "rebooted". (Parecido a procesos zombies).



    Colas de Mensajes (se dejará este material para su interés y estudio personal, No será cubierto por su menor uso frente a otros tópicos.)
    Salte a semáforos.

    Creación de la cola de mensajes

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

    int msgget (key_t key, int msgflg);

    Retorna el identificador de la cola ó -1 en caso de error.


    Para cerrar la cola y efectuar otras tareas de control (como retornar número de mensajes, número de bytes, último proceso en enviar mensaje, último en recibir, etc.)
    msgflg es especificado para asignar permisos de la misma forma como se hace con archivos (ugo).

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

    int msgctl (int msqid, int cmd, struct msqid_ds *buf);

    msqid: valor retornado por msget()
    cmd: IPC_STAT para retornar estructura struct msqid_ds
            IPC_SET para fijar algunos valores de la estructura
            IPC_RMID para remover la cola de mensajes.
    buf: puntero donde retornar valores.

    Envío e recepción de mensajes

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>

    int msgsnd( int msqid, const void * msgp, size_t msgsz, int msgflg);
    int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtype, int msgflg);


    Donde la estructura de cada mensaje es:

    struct msgbuf {
        long    mtype;
        char    mtext[];
    };
    msgflg puede contener la constante IPC_NOWAIT. Su interpretación depende del llamado. En msgsnd  IPC_NOWAIT  retorna y genera un error en caso que la cola no pueda contener más nodos. En otro caso msgsnd se bloquea hasta que haya espacio disponible. En msgrcv retorna inmediatamente y con error en caso que no haya mensajes del tipo especificado para leer.  EN otro caso espera hasta que mensajes de ese tipo sean ingresados.

    msgtype: 0 pide leer el siguiente mensaje.
                > 0 lee el siguiente mensaje de ese tipo.
                < 0 lee el siguiente mensaje con mtype <= |msgtype|

    Hay mas detalles que se pueden estudiar en páginas man (man pages).

    Ejemplo: msq_srv  msq_clnt



    Semáforos
        No son realmente un mecanismo de IPC (como ustedes deben ya saber de sistemas operativos).
    Para obtener acceso a un recurso compartido:
    1. Chequear el valor del semáforo
    2. Si el valor es mayor que 0, se puede usar el recurso. El semáforo es decrementado en 1.
    3. Si el valor en 0, el proceso se va a dormir (bloquea) hasta que el valor sea mayor que 0.
    Creación de semáforo
        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/sem.h>

        int semget(key_t key, int nsems, int semflg);

    Para remover un semáforo o hacer otra tareas de control
        int semctl( int semid, int semnum, int cmd, union semun arg);

    El parámetro cmd puede adoptar los siguientes valores entro otros:
    IPC_STAT:  para leer el estado del semáforo.
    IPC_SET: permite cambiar los permisos de acceso del semáforo.
    IPC_RMID: Permite remover el semáforo.
     

    Para operar el semáforo:
        int semop (int semid, struct sembuf *ops, size_t nops);

        struct sembuf {
            ushort    sem_num;
            short      sem_op;
            short      sem_flg;
        };

    En general estas funciones no son amigables para operar con semáforos debido a:

    1. Se crea un conjunto de semáforos en lugar de sólo uno.
    2. La creación de un semáforo (semget () ) es independiente de su iniciación (semctl() )
    3. El semáforo permanece si ningún proceso lo remueve.
    Por estas razones aquí tienen un archivo que encapsula estas llamadas en otras "más amigables".



    Memoria Compartida
        Memoria compartida permite a dos o más procesos compartir una región de memoria. Ésta es la forma más rápida de comunicación entre procesos porque no hay necesidad de hacer copias de datos. (Ojo con resultado de evaluación hecha por alumnos en tarea año 2000, en la cual no se ratifica esto, puede ser por el tamaño del buffer usado).
        Algún mecanismo de sincronización debe ser empleado para impedir accesos mientras se escribe.

    Creación de una region de memoria compartida

    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>

    int shmget (key_t key, int size, int flag);

    Con esta llamada se crea una estructura de control asociada a esta memoria compartida.

    Esta estructura se puede manipular a través de la función shmctl()
            shmctl ( int shmid, int cmd, struct shmid_ds * buf );

    Uno de los comandos permite remover la region de memoria compartida.

    Para hacer uso de la memoria compartida los procesos deben ligar (attach) el segmento de memoria compartida. Esto se hace con la función shmat()
        void * shmat (int shmid, void *shmaddr, int shmflg);

    shmaddr es la dirrección a la cual la memoria debe ser ligada. Se recomienda especificar 0 (y el sistema elige la mejor dirección).
    Si shmflg contiene el flag SHM_RDONLY, la memoria es de sólo lectura, en otro caso es lectura y escritura.

    Para desligar (detach) la memoria compartida:
        int shmdt( void *shmaddr);

    Donde shmaddr es el valor retornado por shmat().

    Ejemplo:  Productor consumidor usando memoria compartida. Un programa actua como servidor y otro como cliente. El servidor lee información desde la entrada estándar y la transfiere a la memoria compartida. El cliente la lee desde la memoria compartida y la escribe en la salida estándar.

    Otros ejemplos de uso de memoria compartida y semáforos.