Liberar Memoria en Linux usando GCC

Estoy teniendo problemas para liberar un vector que contiene varios punteros los cuales apuntan a buffers de memoria reservados con el malloc. El código es el siguiente:

PHP:
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/soundcard.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <sys/ipc.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>

#define REPETICION  2
#define NUM_FRAMES  20

void GenerarDatosBuffer(unsigned char* buffer)
{
    int y, x, paso;

    for(y=0; y<576; y++)
    {
        paso=y*640;

        for(x=0; x<640; x++)
        {
            *(buffer+paso+x)=x;
        }
    }
}


int main()  //No se tuvo en cuenta la dimension variable de la matrix -> pensado para 3x3
{
    void *buffer[NUM_FRAMES];
    unsigned int cont, cont2;

    for(cont2=0; cont2<REPETICION; cont2++)
    {
        for(cont=0; cont<NUM_FRAMES; cont++)
        {
            buffer[cont]=malloc(640*576*sizeof(char));

            if(!buffer[cont])
                {
                    printf("Error al reservar memoria!\n");
                    return -1;
                }

            GenerarDatosBuffer((unsigned char*)(buffer[cont]));

        }


        for(cont=0; cont<NUM_FRAMES; cont++)
        {
            free(buffer[cont]); //Libero memoria!

            sleep(1);
        }
    }

    return 0;
}

La función "GenerarDatosBuffer" simplemente llena de información el buffer ya que es necesario para ver la memoria ocupada.

El problema radica que a la hora de liberar la memoria de "a buffers", en la primera vuelta se vé que se libera la memoria paulativamente 640x576 bytes por c/segundo. Sin embargo en la segunda vuelta, no se liberá de "a buffers", sino que se libera toda la memoria 20x640x576 bytes de un saque una vez liberado el último elemento, algo que en definitiva no resulta de utilidad, ya que necesito si o si liberar de "a buffers".

Lo mismo ocurre si utilizo listas simplemente enlazadas en vez de un vector de punteros, en la primera vuelta se libera de "a buffers", sin embargo a la segunda vuelta se libera toda la memoria una vez liberado el último elemento.

Es como si el SO intentara hacer un cache del bloque de memoria reutilizado.
 
Tenes activadas las optimizaciones de codigo en el compilador???
Si es asi, desactivalas por completo y vemos.
 
Hagamos una prueba. Libera los punteros a mano, tipo:
free( buffers[0] );
Luego el 1 y asi.... y verifica como lo hace.
Sin optimizaciones...

Ahhh... y que los punteros sean a char y no a void... eso es una cochinada!!
 
Última edición:
Ahhh... y que los punteros sean a char y no a void... eso es una cochinada!!

No seas malo, que no hace ningún daño. :LOL:

Hagamos una prueba. Libera los punteros a mano, tipo:
free( buffers[0] );
Luego el 1 y asi.... y verifica como lo hace.
Sin optimizaciones...

Pasa lo mismo. Incluso en arquitectura ARM me hace eso, que es a donde realmente planeo usar la rutina.
 
No seas malo, que no hace ningún daño. :LOL:
No... solo el despelote de casts explicitos para usar la aritmetica de punteros.
Mis alumnos pierden el 20% del puntaje si hacen eso....

Pasa lo mismo. Incluso en arquitectura ARM me hace eso, que es a donde realmente planeo usar la rutina.
Es muy raro...
Lo otro que podes hacer es probar con un **char y crear dinamicamente el arreglo de punteros y a partir de ahi asignar y liberar memoria de cada puntero.... al menos reducis la posibilidad de que el compilador invente algo...

PD: las opciones de optimizacion son con -O y no con -o
 
No... solo el despelote de casts explicitos para usar la aritmetica de punteros...

Justamente ese casteo obligado me resulta de mucha utilidad para identificar de inmediato el tipo de datos que estoy manejando, de lo contrario tengo que ir a ver la definición.

Por otro lado, resulta fácil manipular los datos según el tipo de datos.

Mis alumnos pierden el 20% del puntaje si hacen eso....

Uuu... aspero el profe. :LOL:

Es muy raro...
Lo otro que podes hacer es probar con un **char y crear dinamicamente el arreglo de punteros y a partir de ahi asignar y liberar memoria de cada puntero.... al menos reducis la posibilidad de que el compilador invente algo...

Yo pensé lo mismo, tal vez el vector de punteros no le gusta, entonces me decidí a probar usando listas simplemente enlazadas, algo así (si, de nuevo puntero void :LOL:):

PHP:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

#define REPETICION  3
#define FRAMES  20

struct ListaBuffer
{
    void *buffer;
    struct ListaBuffer *buff_sig;
};

void GenerarDatosBuffer(unsigned char* buffer)
{
    int y, x, paso;

    for(y=0; y<576; y++)
    {
        paso=y*640;

        for(x=0; x<640; x++)
        {
            *(buffer+paso+x)=x;
        }
    }
}

int CrearLista(struct ListaBuffer **lista, unsigned int num_elementos)
{
    struct ListaBuffer *lista_ant, *lista_act;
    unsigned int cont;

    for(cont=0; cont<num_elementos; cont++)
    {
        lista_act=(struct ListaBuffer *)malloc(sizeof(struct ListaBuffer)); //Creo un nuevo elemento
        if(lista_act<0)
            return -1;  //Deberia liberar la memoria!

        lista_act->buffer=malloc(640*576*sizeof(char));  //1 frame!
        if(lista_act->buffer<0)
            return -1;  //Deberia liberar la memoria!

        GenerarDatosBuffer((unsigned char*)(lista_act->buffer));
     
        if(cont==0) //1er elemento!
        {
            *lista=lista_act;
            lista_ant=lista_act;
        }
        else
        {
            lista_ant->buff_sig=lista_act;  //Enlazo la lista actual con la anterior!
            lista_ant=lista_act;
        }
    }

    lista_act->buff_sig=NULL;

    return 1;
}

void LiberarLista(struct ListaBuffer *lista, unsigned int num_elementos)
{
    struct ListaBuffer *lista_ant, *lista_act;
    unsigned int cont;
    unsigned char flag_terminar=0;

    for(cont=0; cont<num_elementos; cont++)
    {
        if(cont==0)
        {
            if(lista->buff_sig!=NULL)
                lista_act=lista->buff_sig;
            else
                flag_terminar=1;

            free(lista->buffer);
            free(lista);
        }
        else
        {
            lista_ant=lista_act;
            lista_act=lista_ant->buff_sig;

            if(lista->buff_sig!=NULL)
                lista_act=lista_ant->buff_sig;
            else
                flag_terminar=1;

            free(lista_ant->buffer);
            free(lista_ant);
        }

        if(flag_terminar)
            break;

        sleep(1);
    }
}

int main()
{
    struct ListaBuffer *lista=NULL;
    int cont;

    for(cont=0; cont<REPETICION; cont++)
    {
        if(CrearLista(&lista, FRAMES)<0)
        {
            printf("Error al crear la lista\n");
            return -1;
        }
        else
            printf("La lista se ha creado\n");

        LiberarLista(lista, FRAMES);

        printf("La lista se libero\n");
        sleep(1);
    }

    return 0;
}

Y hace exactamente lo mismo, en el primer for va liberando las listas, en segundo hasta no liberar la última, el SO vé ocupada toda la memoria de las listas y recién ahí libera la memoria de golpe, lo mismo sucede con la tercera iteración.

PD: las opciones de optimizacion son con -O y no con -o

Es cierto lo que decís y de hecho lo hice de esa forma, me generó un "a.out" que es ejecutable. También aplique el -O0 para el código de las listas.

Es raro que no te deje liberar de "a buffer" y por más que busqué no encontré nada. La otra alternativa es usar mmap y ummap (funciones que supuestamente usa malloc y free para el mapeo de memoria), pero fracasé estrepitosamente.
 
Última edición:
Ahora si que no entiendo... :confused: :confused:
Vos estás creado una lista (o un arreglo) de 20 "buffers" de 640*576 bytes cada uno. Les asignás memorias, los usás una vez y le liberás la memoria, y esto lo repetís 3 veces. Y decís que la primera vez libera los buffers de a uno y la segunda y tercera libera todo de golpe?????

Si ese es tu problema, yo no le daría mucha bola, por que no hay garantías de la secuencia de operaciones que usa el SO para recuperar la memoria, a menos que sea un SO de tiempo real. Solo importa que cuando la necesités la vas a tener o va a fallar el malloc.

Y te pregunto: para que hacés semejante lío de asignación y liberación de memoria????. No podés darle memoria a los buffers y liberarla cuando ya no sea necesaria, reutilizándola la mayor cantidad de veces posibles????
 
El tema es que la cantidad de memoria en realidad son para 450 buffers, que los utilizo para grabar casi 18 seg de video en 25fps, si trabajo con archivos la tasa cae demasiado.

Una vez que se termina de grabar, recién ahí vuelco la memoria en archivo, como consecuencia de esto, Linux a la hora de manejar archivos usa la memoria RAM disponible como memoria cache de los archivos (para no tener que levantar de nuevo el archivo en disco, o en este caso en flash, esto no se puede evitar), por lo tanto si yo libero toda la memoria después de pasarlo archivos, llegó a un punto en donde le exijo al sistema una memoria equivalente a 900 buffers (si una cosa de locos). Esto implica que en un momento el sistema solo tiene 45MB de RAM (de 512MB total que tiene el sistema) y si supero ese tamaño disponible, el sistema se cuelga.

Entonces en la primera vuelta al recuperar de "a buffers" y al volcar en archivo, compenso el aumento de memoria dejandolo siempre en 450 buffers, sin embargo en la 2da vuelta eso no sucede y alcanzo ese piso de 45MB disponibles.
 
No entiendo nada... :confused: :confused:
Vos tenés que usar 450 buffers para capturar un cuadro de video en cada uno, OK??
Eso luego lo grabás en uno o mas archivos, OK? Listo... ya tenés los buffers sin utilizar, por que mandaste todo a disco, así que reusá los mismos buffers de nuevo (seguís teniendo 450... de donde sacás los 900???), los volvés a llenar de cuadros de video y los bajás a disco, y vuelta a lo mismo.... todas las veces que sea necesario sin asignar ni liberar memoria.
Si tenés problemas por que cachea muchos archivos, pues grabá uno solo gigante con todo el video y luego recuperá cada frame, ya que tienen un tamaño perfectamente definido.
O usá menos buffers y grabá mas seguido.
El problema es que te estás chupando casi 170Mb de memoria para los 450 buffers y tal vez puedas hacerlo con 150 o con 75 buffers y grabando mas seguido, tipo pipeline. De más está decir que necesitás una SD clase 10 para grabar a esa velocidad.
 
Vos tenés que usar 450 buffers para capturar un cuadro de video en cada uno, OK??

Si señor, c/cuadro es de 640x576.

Eso luego lo grabás en uno o mas archivos, OK?

Primero capturo los 450 cuadros y una vez finalizada la captura, lo paso a archivos (no me dá el tiempo de pasar de RAM a archivo y obtener los 25 frames por segundos).

Listo... ya tenés los buffers sin utilizar, por que mandaste todo a disco, así que reusá los mismos buffers de nuevo (seguís teniendo 450... de donde sacás los 900???)los volvés a llenar de cuadros de video y los bajás a disco, y vuelta a lo mismo.... todas las veces que sea necesario sin asignar ni liberar memoria.

Tengo los 450 frames originales alojados en ram. Cuando trabajas con archivos en Linux, ya sea abrir un archivo, o grabarlo, el tipo si o si graba en memoria RAM dicho archivo para "cachearlo".

Es decir que el proceso una vez capturados los 450 frames es el siguiente:

1- Empiezo a leer el frame 1 alojado en RAM.
2- Llamo a una función que graba dicho frame en un archivo. En estas condiciones tengo el frame original en RAM + la copia en cache producida por el archivo.
3- Libero el frame orginal en RAM, en la primera vuelta, solo queda alojada en RAM un solo frame (el cache), pero en el resto de las iteraciones, eso no sucede y terminan quedando los dos frames en RAM.
4 (cuando estoy en la iteración >2)- Antes de liberar el último frame, tengo alojado en RAM 900 frames (450 frames originales + 450 en cache) y al liberar el último frames, mágicamente se liberan los 450 frames originales alojados en RAM.

Para más detalles del cache, si en consola ponés "free -h", aparece un sub-item llamado "cached" que va creciendo a medida que trabajás con archivos. En algún momento hay que liberar dicho "cache" mediante el uso por ej del siguiente comando ""sync && echo 3 > /proc/sys/vm/drop_caches". Si bien parece algo de loco, es algo propio de Linux que no puedo modificar. La idea es que el SO utilice la RAM en desuso y la aproveche "cacheando" en RAM la información que había en disco. Se supone que si Linux "cachea" en forma automática, también debería liberar en forma automática, pero esto último no lo hace, la aplicación debe estar pendiente todo el tiempo de no pasarse (lo que parece muuuy extraño en un SO).

Podría ir liberando el "cache" a medida que voy volcando los frames en archivo, pero esa "liberación" es lenta y provoca que el proceso sea muy lento. También podría hacer la liberación c/100frames (o algo así), pero es una solución muy chancha.

¿Estoy grabando todo el tiempo?

No, solo grabo 18s cuando el usuario lo exige.

¿Por qué se liberan los buffers?

Porque en el estado "normal" esos buffers no se emplean, solo se usan al momento de grabar.

¿Podría usar los buffer sin liberarlos?

Si, pero durante el estado "normal" del programa, estaría pidiendo 150MB sin sentido. De todas formas, seguiría teniendo el problema tener 900 buffer alojados en RAM al mismo tiempo.

El problema está en no poder liberar esos buffers en forma individual a la segunda grabación que exige el usuario y eso resulta en un comportamiento raro del SO, como si dijiera, "ok, ¿otra vez me pedís RAM?, toma, pero por las dudas la libero cuando liberes el bloque completo (los 450 buffers)".

Si tenés problemas por que cachea muchos archivos, pues grabá uno solo gigante con todo el video y luego recuperá cada frame, ya que tienen un tamaño perfectamente definido.

No importa si es un archivo grande o muchos pequeños, el cache se llena de la misma forma.

El problema es que te estás chupando casi 170Mb

Está asumido que sea así, se supone que la RAM me dá, tengo casi 360MB libres en el sistema (incluyendo mi aplicación sin los 450 buffer).

De más está decir que necesitás una SD clase 10 para grabar a esa velocidad.

Eso fué/es un problema, a la hora de pasar los archivos con un pendrive USB trabajo a 20-17 fps, en cambio con un SD clase 4 (lentísima) a más de 12 fps no llega.
 
Última edición:
Ok. Entonces el problema no es del programa sino del Linux que no libera el cache en forma automatica.
La pregunta ahora es: estas repitiendo los nombres de los archivos entre cada grupo de grabaciones???
 
Si, los grabo en forma genérica como "Frame_%05u.bmp" en carpetas distintas, para que terminen con un formato tipo "Frame_00001.bmp" (exageré un poco con los ceros :LOL:).

Por ej. a la hora de reproducir, lo hago mediante esos bmp, entonces en caso de no encontrarse en "cache", la primera vuelta se nota que vá más lento, pero a partir de la segunda, como se encuentran "cacheados" en RAM, la reproducción se vuelve mucho más rápida. Esto lo hace Linux por su cuenta.
 
¿No simplificaría el asunto reservar/liberar una matriz tridimensional? Porque, realmente, estamos hablando de eso.

Reducirías todos los malloc() a uno solo.
 
A ver... tirate un ls -a en /proc/sys/vm para ver que hay por ahí...

No tengo la plaqueta, pero mañana te comento que devuelve.

Que versión de kernel estás corriendo?

Es un Debian con un kernel v3.8.13.

PD:
Encontré eso: http://insights.oetiker.ch/linux/fadvise/
Tal vez sea lo que necesitás...

Gracias, le voy a dar una leída y después comento.

JoaquinFerrero dijo:
¿No simplificaría el asunto reservar/liberar una matriz tridimensional? Porque, realmente, estamos hablando de eso.

Reducirías todos los malloc() a uno solo.

Hacer eso implicaría trabajar con un bloque de memoria continuo, habría que ver si el sistema tiene esa memoria disponible (supongo que si), sin embargo esto justamente no me permitiría liberar la memoria de a frame, obligandome a trabajar si o si con los 150-160MB de un saque.

Por otro lado como menciona el Dr., está el problema del cache en disco, que al no poder liberar de a frame, termino con 300-320MB de RAM ocupados.
 
Linux maneja muy bien la memoria: si le pedimos memoria para nuestro programa, intentará obtenerla a costa de reducir el caché de disco. Y al revés: si usamos mucha caché de disco, seguirá aumentándola mientras haya memoria libre.

De todas maneras, este comportamiento se puede modificar tocando los parámetros que hay en /proc/sys/vm/ . Cuestión de buscar en la documentación, ya que depende de cada versión de Linux.
 
Última edición por un moderador:
Atrás
Arriba