Memoria compartida en Linux

La memoria compartida es uno de los mecanismos de Comunicación Entre Procesos (IPC) que hay en Linux.

Para usar memoria compartida en Linux es necesario seguir una serie de pasos que luego se traducen a llamadas al sistema.
  1. Necesitamos obtener un identificador de IPC. Para ello convertimos una ruta (path) del sistema en un identificador IPC. Este identificador es necesario para crear la crear la zona de memoria virtual. Esto es muy sencillo de hacer con la llamada al sistema ftok.
  2. Crear el segmento de memoria compartida con la llamada al sistema shmget.
  3. Operar con la memoria compartida. Indicamos lo que queremos compartir con la llamada al sistema shmat.
  4. Destruimos el segmento de memoria compartida con la llamada al sistema shmdt y shmctl.
Obtener el identificador IPC

Modo de uso:
#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname, int proj_id);

El primer parámetro es una ruta. Lo que se puede hacer es coger una ruta que siempre esté en el sistema, por ejemplo "/bin/cp". El segundo parámetro es un número aleatorio, pero que conozcan todos los procesos que quieren unirse al mismo segmento de memoria compartida (al igual que la ruta).
Por ejemplo podemos hacer:
int key = ftok ("/bin/cp", 123);
Información más detallada:
$ man ftok

Crear el segmento de memoria compartida

Esto lo hacemos con la llamada al sistema shmget.

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

int shmget(key_t key, size_t size, int shmflg);

  • El primer parámetro es la clave obtenida en la llamada al sistema anterior.
  • El segundo parámetro es el tamaño de la memoria a compartir dados en bytes.
  • El tercer parámetro son flags para el modo de uso y quién accede al segmento de memoria.

Los flags son: 
    • IPC_CREAT:  crea un nuevo segmento.
    • IPC_EXCL: se combina con IPC_CREAT y si el segmento ya existía, entonces la creación de segmento falla.
    • mode_flags: son los permisos del segmento sobre usuario, grupo y otros, además de los permisos especiales.
    Esta función retorna -1 si hubo error y actualiza la variable errno.

    Por ejemplo, creamos una zona de memoria compartida para 10 enteros

    int id = shmget(key, sizeof(int) * 10, 0777 | IPC_CREAT);

    Información más detallada:
    $ man shmget


    Operaciones con la memoria compartida

    Ahora tenemos que unir nuestros datos al segmento de memoria compartida. Esto es lo que hace la llamada al sistema shmat. Se le especifica el ID obtenido en shmget:

    void *shmat(int shmid, const void *shmaddr, int shmflg);
    

    La dirección de memoria compartida a la que se asocia depende del parámetro shmaddr y shmflg. Para más información sobre estos dos parámetros hacer un man shmat.
      Para el uso que le vamos a dar, bastará con poner estos dos parámetros a 0, con esto le indicamos que el sistema coja una dirección apropiada donde fijar el segmento.

      int *array;
      array = shmat(id, 0, 0);

        Desasignar la memoria compartida

        Cuando ya hayamos acabado de usar la memoria compartida, se debe liberar el segmento "cogido". Esto se hace con 2 llamadas al sistema: shmdt y shmctl
        • shmdt: lo que hace es que desasocia la zona de datos del programa con la memoria compartida. 
        • shmctl: sirve para pasar recibir información de la memoria compartida, o para establecer usuario y permisos del segmento. También sirve para destruir el segmento de memoria compartida. 
        Ejemplos de uso:
          /* Free the shared memory */
          shmdt ((char *)array);
          shmctl (id, IPC_RMID, (struct shmid_ds *)NULL);

          El parámetro IPC_RMID lo que hace es eliminar el segmento de memoria compartida.


          Ejemplo

          Se tienen 2 programas. Uno cliente y otro servidor (si se quiere ver así). El servidor crea la memoria compartida y sirve un dato. El cliente se acopla a la memoria compartida y recoge el dato.

          /* p1.c -- demostrate the mecanism of
           *         shared memory
           * blog   : snatverk.blogspot.com
           * date   : Tue Sep 14 10:54:37 WEST 2010
           * compile: $ gcc p1.c -o p1
           */
          
          #include <stdio.h>
          #include <sys/types.h>  
          #include <sys/ipc.h>
          #include <string.h>
          #include <errno.h>
          #include <sys/shm.h> /* shm*  */
          
          #define FILEKEY "/bin/cat"
          
          #define KEY 1300
          #define MAXBUF 10
          
          int main () {
           /* Key to shared memory */
             int key = ftok(FILEKEY, KEY);
             if (key == -1) {
                fprintf (stderr, "Error with key \n");
                return -1; 
             }    
             /* we create the shared memory */
             int id_zone = shmget (key, sizeof(int)*MAXBUF, 0777 | IPC_CREAT);
             if (id_zone == -1) {
                fprintf (stderr, "Error with id_zone \n");
                return -1; 
             } 
             printf ("ID zone shared memory: %i\n", id_zone);  
           
             int *buffer; /* shared buffer */
             /* we declared to zone to share */
             buffer = shmat (id_zone, (char *)0, 0);
             if (buffer == NULL) {
                fprintf (stderr, "Error reserve shared memory \n");
                return -1; 
             }
           
             printf ("Pointer buffer shared memory: %p\n", buffer); 
             int i;
             for (i = 0; i < MAXBUF; i++) 
                buffer[i] = i;
             /* The daemon executes until press some character */
             char c;
             c = getchar();
           
             /* Free the shared memory */
             shmdt ((char *)buffer);
             shmctl (id_zone, IPC_RMID, (struct shmid_ds *)NULL);
             return 0;
          }
          

          ___________________________________________________
          El segundo proceso:

          /* p2.c -- demostrate the mecanism of
           *         shared memory
           * blog   : snatverk.blogspot.com
           * compile: $ gcc p2.c -o p2
           */
          
          #include <stdio.h>
          #include <sys/types.h>
          #include <sys/ipc.h>
          #include <string.h>
          #include <errno.h>
          #include <sys/shm.h> /* shm*  */
          
          #define FILEKEY "/bin/cat"
          #define KEY 1300
          #define MAXBUF 10
          
          int main () {
             /* Key to shared memory */
             int key = ftok(FILEKEY, KEY);
             if (key == -1) { 
                fprintf (stderr, "Error with key \n");
                return -1; 
             }
          
             /* we create the shared memory */
             int id_zone = shmget (key, sizeof(int)*MAXBUF, 0777 | IPC_CREAT);
             if (id_zone == -1) {
                fprintf (stderr, "Error with id_zone \n");
                return -1; 
             }
          
             printf ("ID zone shared memory: %i\n", id_zone);
          
             int *buffer; /* shared buffer */
             /* we declared to zone to share */
             buffer = shmat (id_zone, (char *)0, 0);
             if (buffer == NULL) { 
                fprintf (stderr, "Error reserve shared memory \n");
                return -1; 
             }
           
             printf ("Pointer buffer shared memory: %p\n", buffer);
          
             /* Write the values of shared memory */
             int i;
             for (i = 0; i < MAXBUF; i++) 
                printf ("%i\n", buffer[i]);
             return 0;
          }
          

          2 comentarios:

          RaMpAgE dijo...

          Hola, me gustaria saber si tienes informacion sobre la funcion shmctl () la necesito para bloquear espacios de memoria para poder sincronizar.

          Saludos

          snatverk dijo...

          Hola RaMpAGE. Pues como siempre tienes el manual de consola:
          $ man shmctl

          También he encontrado un pequeña presentación que creo que te puede ayudar:

          http://www.slideshare.net/orlandoaleortiz/memoria-compartida

          Por lo que he leído, al shmctl le pasas SHM_LOCK para bloquear y SHM_UNLOCK para desbloquear la región.

          Publicar un comentario en la entrada