2009-12-10 20 views
10

Estoy desarrollando una aplicación destinada a sistemas de escritorio que pueden tener tan solo 256MB de RAM (Windows 2000 en adelante). En mi aplicación tengo este archivo grande (> 256MB) que contiene registros fijos de aproximadamente 160 bytes/cada uno. Esta aplicación tiene un proceso bastante largo en el que, con el tiempo, accederá aleatoriamente a aproximadamente el 90% del archivo (para leer y escribir). Cualquier escritura de registro dada no tendrá más de 1,000 accesos de registro de la lectura de ese registro en particular (puedo ajustar este valor).CreateFileMapping, MapViewOfFile, cómo evitar mantener la memoria del sistema

Tengo dos opciones obvias para este proceso: E/S regular (FileRead, FileWrite) y asignación de memoria (CreateFileMapping, MapViewOfFile). Este último debería ser mucho más eficiente en sistemas con suficiente memoria, pero en sistemas con poca memoria intercambiará la mayor parte de la memoria de otras aplicaciones, lo que en mi aplicación es un no-no. ¿Hay alguna manera de evitar que el proceso consuma toda la memoria (por ejemplo, forzar el vaciado de las páginas de memoria a las que ya no accedo)? Si esto no es posible, entonces debo recurrir a la E/S regular; Me hubiera gustado usar E/S superpuestas para la parte de escritura (ya que el acceso es tan aleatorio), pero la documentación dice writes of less than 64K are always served synchronously.

Se agradecen todas las ideas para mejorar la E/S.

+0

¿Quizás VirtualFree (MEM_DECOMMIT) puede ser de ayuda? No estoy familiarizado con eso. –

+1

No, VirtualFree (MEM_DECOMMIT) falla para los archivos MMF; Acabo de verificar. –

Respuesta

10

Finalmente encontré el camino, derivado de un hilo here. El truco es usar VirtualUnlock() en los rangos que necesito para no comprometerme; aunque esta función devuelve FALSE con el error 0x9e ("El segmento ya está desbloqueado"), la memoria se libera realmente, incluso si las páginas se modificaron (el archivo se actualizó correctamente).

Aquí es mi programa de prueba de muestra:

#include "stdafx.h" 

void getenter(void) 
{ 
    int  ch; 
    for(;;) 
    { 
     ch = getch(); 
     if(ch == '\n' || ch == '\r') return; 
    } 
} 

int main(int argc, char* argv[]) 
{ 
    char* fname = "c:\\temp\\MMFTest\\TestFile.rar";  // 54 MB 
    HANDLE hfile = CreateFile(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL); 
    if(hfile == INVALID_HANDLE_VALUE) 
    { 
     fprintf(stderr, "CreateFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     return 1; 
    } 

    HANDLE map_handle = CreateFileMapping(hfile, NULL, PAGE_READWRITE | SEC_RESERVE, 0, 0, 0); 
    if(map_handle == NULL) 
    { 
     fprintf(stderr, "CreateFileMapping() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(hfile); 
     return 1; 
    } 

    char* map_ptr = (char*) MapViewOfFile(map_handle, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0); 
    if(map_ptr == NULL) 
    { 
     fprintf(stderr, "MapViewOfFile() error 0x%08x\n", GetLastError()); 
     getenter(); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Memory usage here is 704KB 
    printf("Mapped.\n"); getenter(); 

    for(int n = 0 ; n < 10000 ; n++) 
    { 
     map_ptr[n*4096]++; 
    } 

    // Memory usage here is ~40MB 
    printf("Used.\n"); getenter(); 

    if(!VirtualUnlock(map_ptr, 5000 * 4096)) 
    { 
     // Memory usage here is ~20MB 
     // 20MB already freed! 
     fprintf(stderr, "VirtualUnlock() error 0x%08x\n", GetLastError()); 
     getenter(); 
     UnmapViewOfFile(map_ptr); 
     CloseHandle(map_handle); 
     CloseHandle(hfile); 
     return 1; 
    } 

    // Code never reached 
    printf("VirtualUnlock() executed.\n"); getenter(); 

    UnmapViewOfFile(map_ptr); 
    CloseHandle(map_handle); 
    CloseHandle(hfile); 

    printf("Unmapped and closed.\n"); getenter(); 

    return 0; 
} 

Como se puede ver, el conjunto de trabajo del programa se reduce después de ejecutar VirtualUnlock(), al igual que yo necesitaba. Solo necesito hacer un seguimiento de las páginas que cambio para desbloquearlas según corresponda.

+0

Genial, gracias por compartir esto. Estaba buscando desesperadamente una forma de controlar la cantidad de memoria utilizada por los archivos mapeados en la memoria, y ya estaba perdiendo la esperanza de que se pueda hacer. Solo espero que MS no desactive este es un sistema operativo futuro. Después de todo, ¿por qué no podemos simplemente comprometer y liberar las páginas como se desee? – Suma

+0

Nota: el código de error que describe parece ser esperado según la observación del doc. VirtualUnlock: "Si alguna de las páginas en el rango especificado no está bloqueada, VirtualUnlock elimina dichas páginas del conjunto de trabajo, establece el último error en ERROR_NOT_LOCKED y devuelve FALSO ". – Suma

+0

Una nota más: el experimento es algo engañoso. El conjunto de trabajo del proceso se reduce, pero eso no necesariamente significa que la página se descarta. Si va a mapear de nuevo, puede ver que lo obtiene de inmediato, sin ninguna actividad de archivo de página. Ver mi experimento en http://stackoverflow.com/questions/3525202/how-can-i-decommit-a-file-mapped-page/3525266#3525266 – Suma

1

¿Está mapeando todo el archivo como un bloque con MapViewOfFile? Si es así, intente mapear partes más pequeñas. Puede vaciar una vista con FlushViewOfFile()

+0

No accedo al archivo secuencialmente; hay un patrón aleatorio que no puedo controlar para acceder al archivo, por lo que el mapeo de partes pequeñas será muy poco efectivo. Además, FlushViewOfFile() no libera ninguna memoria; solo fuerza la escritura de las páginas sucias. –

3

Simplemente asigne todo el archivo a la memoria. Esto consume memoria virtual pero no física. El archivo se lee del disco por partes y es expulsado de la memoria por las mismas políticas que rigen el archivo de intercambio.

+4

Bueno, ese es precisamente el problema. Las políticas que rigen el archivo de intercambio tienden a mantener los contenidos en la memoria mucho más que los de la memoria caché de archivos, por lo que el acceso al archivo asignado finalmente intercambiará la mayoría de los otros procesos. –

2

VirtualUnlock no parece funcionar. Lo que debe hacer es llamar al FlushViewOfFile (map_ptr, 0) inmediatamente antes de UnmapViewOfFile (map_ptr). El Administrador de tareas de Windows no mostrará el uso de la memoria física. Use ProcessExplorer de SysInternals

Cuestiones relacionadas