Memoria Compartida en C y GNU/Linux
By: Date: enero 2, 2008 Categories: Programación

La memoria compartida es muy útil para programas que subprocesos con fork, sin embargo no necesariamente es ese su límite, la memoria compartida igual puede ser usada por programas totalmente distintos como un medio para enviarse señales y compartir información en común.

Solo hay un problema, todo kernel decente tiene protección de memoria, esto significa que un programa no puede interferir con otro, así que el hecho de que 2 o más programas intenten entrar a la misma zona de memoria es contradictorio, por lo que el sistema operativo así de gusto no lo va a permitir, salvo sea el mismo sistema operativo quien nos ayude a esto.

Para mostrar cómo es posible hacer esto, hare 3 programas, podrían ser al menos 2, pero la intención es mostrar que muchos programas pueden operar con los mismos segmentos de memoria.

El primer programa reservada la memoria y será el encargado de liberarla también, mientras este programa este corriendo los otros 2 podrán accesar a la memoria compartida.

El segundo programa accesara a la memoria compartida e irá haciendo modificaciones en dicha zona, será un contador.

El tercer programa leerá la zona de memoria y nos mostrara el valor actual que hay en ella.

Los 3 programas serán muy similares, las funciones se explican a continuación.

Para que programas completamente independientes compartan información, deben tener un acceso común a un recursos del sistema, estos programas crean si no existe un archivo en el temporal y se ligaran a el para crear una llave

La llave se asigna a una variable tipo key_t usando la función ftok, ftok requiere 2 parametros , el archivo en común que hemos creado y un entero, esto debe coincidir en cada programa que vaya a usar la zona de memoria, de lo contrario el sistema operativo les negara el acceso tal cual debe ser.

Obtenemos la clave con el siguiente ejemplo.

key_t clavecompartida = ftok (“/tmp/acompartido”,33);

una vez con la clave solicitamos al sistema operativo cree una zona de memoria de 400 bytes, un apuntador tipo char de 100 veces su tamaño. Esto nos devolverá un entero con el identificador del segmento de memoria, si la asignación tuvo éxito.
int mem = shmget (clavecompartida,sizeof(char *)*100,0777 | IPC_CREAT);

Nótese que la protección de memoria es similar a la protección de acceso a archives, el parámetro 0777 tiene el mismo significado que los permisos de archivo, de esta forma estamos declarando que nuestra memoria será de acceso total para todos, también podríamos dar permisos de solo lectura para determinados programas que corran con X usuario, como solo lectura o negarle el acceso a cualquier otro usuario que quiera entrar a nuestra memoria compartida, el IPC_CREAT Solo lo usamos al momento de buscar reservar o crear el espacio, para leer o modificar la memoria en otros programas no será necesario, porque ya existirá.

Ya con el identificador entraremos a la zona de memoria vía punteros, al puntero lo alimentamos con la función shmat a la cual le pasamos como parámetro el identificador de nuestra zona de memoria

char *mcompartida = (char *) shmat (mem,NULL,0);

En este punto, el puntero char mcompartida está ligado a la memoria compartida y lo que le hagamos a ese puntero, lo estamos haciendo en esa zona de memoria.

Aclarando que es mas fácil mover cadenas de caracteres por ese punto, por lo que si queremos pasar números, por ejemplo un 10, sera mas fácil enviar una cadena de caracteres “10” y al recibirla en otro programa, convertirla en un entero, por ejemplo

Strcpy (mcompartida,”10”);

Liberando la memoria compartida y borrando el archivo en común, con la función shmctl y pasándole el primer parámetro la variable entera que tiene el identificador de la zona de memoria

shmctl (mem, IPC_RMID, (struct shmid_ds *)NULL);

unlink (“archivo”);

unlink borra archives del sistema operativo

Si el programa terminara de forma incorrecta o por interrupción del usuario, la memoria compartida se quedara en el sistema operativo indefinidamente, ya que el programa nunca le notifico al sistema operativo que debía liberarla.

En el caso de GNU/Linux los comandos ipcs nos ayudaran a administrar manualmente las zonas compartidas de memoria.

Si queremos ver cuántas memorias compartidas hay, usamos el comando ipcs

Por ejemplo si queremos borrar la zona 1234 usamos el Ipcrm –m 1234

Ahora el código de 3 programas que demuestran el acceso a la memoria compartida.

Asignar.c Este programa reserva y libera la memoria, mientras se ejecute los otros 2 programas tendrán acceso a la memoria.
#include <stdio.h>
#include <sys/shm.h>

int main ()
{
        FILE *archivocompartido;
        key_t clavecompartida;
        int mem = 0;
        char *mcompartida = NULL;

        archivocompartido = fopen
(“/tmp/acompartido”,”w+”);
        clavecompartida = ftok
(“/tmp/acompartido”,33);

        mem = shmget
(clavecompartida,sizeof(char *)*100,0777 | IPC_CREAT);
        mcompartida = (char *) shmat
(mem,NULL,0);

        printf (“Creando el segmento
%d de memoria compartida\n\n”,mem);
        printf (“Precione ENTER para
liberar los recursos compartidos\n”);

        getchar ();

        shmctl (mem, IPC_RMID,
(struct shmid_ds *)NULL);
        unlink (“/tmp/acompartido”);
        return 0;
}

Modificar.c Este programa modifica la zona de memoria asignada por asignar.c
#include <stdio.h>
#include <sys/shm.h>

int main ()
{
        FILE *archivocompartido;
        key_t clavecompartida;
        int mem = 0;
        int contador = 0;
        char *mcompartida = NULL;

        archivocompartido = fopen
(“/tmp/acompartido”,”w+”);
        clavecompartida = ftok
(“/tmp/acompartido”,33);

        mem =  shmget
(clavecompartida,sizeof(char *)*100,0777);
        mcompartida = (char *) 
shmat (mem,NULL,0);

        printf (“Trabjando con el
segmento: %d\n”,mem);
        printf (“Cada segundo se
actualiza el valor compartido, el programa dura un minuto\n”);

        while (contador != 60)
        {
               
contador = contador + 1;
               
sprintf (mcompartida,”%d”,contador);
               
sleep (1);
        }

        return 0;
}
Obtener.c este programa obtiene el valor actual de la zona de memoria compartida

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

int main ()
{
        FILE *archivocompartido;
        key_t clavecompartida;
        int mem = 0;
        char *mcompartida = NULL;

        archivocompartido = fopen
(“/tmp/acompartido”,”w+”);
        clavecompartida = ftok
(“/tmp/acompartido”,33);

        mem =  shmget
(clavecompartida,sizeof(char *)*100,0777);
        mcompartida = (char *) 
shmat (mem,NULL,0);

        printf (“Trabajando con el
segmento: %d\n”,mem);
        printf (“El valor compartido
es: %s\n”, mcompartida);

        return 0;
}

Un video mostrando la ejecución de los 3 códigos.

Codigo fuente
https://www.lastdragon.net/misarchivos/memcompartida.tar.gz

14 thoughts on “Memoria Compartida en C y GNU/Linux

  1. Mozilla Firefox 2.0.0.11 Ubuntu Linux

    Intenté probar tus ejemplos, pero el código del tercer programa me marca un error, durante la compilación, el siguiente:

    dmind@VIKI:~/mem$ gcc obtener.c -o obtener
    obtener.c:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘.’ token
    In file included from /usr/include/_G_config.h:44,
    from /usr/include/libio.h:32,
    from /usr/include/stdio.h:72,
    from obtener.c:3:
    /usr/include/gconv.h:72: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/gconv.h:88: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/gconv.h:97: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/gconv.h:174: error: expected specifier-qualifier-list before ‘size_t’
    In file included from /usr/include/stdio.h:72,
    from obtener.c:3:
    /usr/include/libio.h:328: error: expected specifier-qualifier-list before ‘size_t’
    /usr/include/libio.h:360: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/libio.h:369: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/libio.h:485: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘_IO_sgetn’
    In file included from obtener.c:3:
    /usr/include/stdio.h:306: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/stdio.h:313: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/stdio.h:355: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/stdio.h:357: error: el argumento de la cadena de formato no es del tipo cadena de texto
    /usr/include/stdio.h:359: error: expected declaration specifiers or ‘…’ before ‘size_t’
    /usr/include/stdio.h:608: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘fread’
    /usr/include/stdio.h:614: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘fwrite’
    /usr/include/stdio.h:636: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘fread_unlocked’
    /usr/include/stdio.h:638: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘fwrite_unlocked’
    In file included from /usr/include/sys/shm.h:31,
    from obtener.c:4:
    /usr/include/bits/shm.h:52: error: expected specifier-qualifier-list before ‘size_t’
    In file included from obtener.c:4:
    /usr/include/sys/shm.h:54: error: expected declaration specifiers or ‘…’ before ‘size_t’
    obtener.c: En la función ‘main’:
    obtener.c:14: aviso: el paso del argumento 2 de ‘shmget’ crea un entero desde un puntero sin una conversión
    obtener.c:14: error: demasiados argumentos para la función ‘shmget’

  2. Mozilla Firefox 2.0.0.8 Fedora Linux

    Sólo hay que comentar que aunque el sistema operativo administre la memoria, no quiere decir que sepa como nuestra aplicación la distribuye lógicamente; por lo que si almacenamos 2 o mas tipos de datos (variables) que tengan relación, puede que existan problemas si son modificados concurrentemente. Para evitarlo es necesario el uso de semáforos.

  3. Internet Explorer 7.0 Windows Vista

    como dice Jorge, en este ejemplo solo un programa modifica y otro lee, lo cual no es mucho problema, pero si mas programas intentan entrar a modificar la misma zona de memoria. habria que usar semaforos, como si fuera programacion multihilo

  4. Mozilla Firefox 3.6.3 Linux

    Hola, muy bueno tu post. Tome el ejemplo para hacer algo de la facultad y te hago una consulta, como hago para que me muestre el proceso que esta compartiendo memoria en el maps?
    la idea es hacer cd /proc/1
    y luego vi maps
    Asi poder observar la memoria compartida con el permiso correspondiente.
    Gracias desde ya!

  5. Google Chrome 33.0.1750.146 Linux

    @Assault: Que pelotudo de cuarta que sos man… mira lo que venis a corregir… el tipo te enseña a hacer algo que evidentemente no tenes ni puta idea de como hacer, lo prueba el comentario tan pelotudo que hiciste.. mira en lo que te fijaste, IMBECIL
    Hacele un favor a la vida y pegate un tiro en la cabeza salame, no servis ni para donacion de organos.

    Gracias al creador del posto por la mano..!

  6. Google Chrome 35.0.1916.114 Linux

    Buen ejemplo, muchas gracias. Estoy siguiendo el libro Programación en Linux con ejemplos de Kurt Wall pero no me quedaba claro, he encontrado este post más que recomendable y queda todo claro. Un gran trabajo, muchas gracias.

Responder a Danilo Cancelar respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *