Memoria Compartida en C y GNU/Linux

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 Comments

Add a Comment

Comment spam protected by SpamBam