2010-05-26 5 views
10

He estado trabajando con grandes archivos dispersos en openSUSE 11.2 x86_64. Cuando trato de mmap() un archivo escaso de 1TB, falla con ENOMEM. Hubiera pensado que el espacio de direcciones de 64 bits sería adecuado para mapear en un terabyte, pero parece que no. Experimentando aún más, un archivo de 1GB funciona bien, pero un archivo de 2GB (y algo más grande) falla. Supongo que podría haber un entorno en el que ajustar, pero una búsqueda exhaustiva no arroja nada.¿Por qué mmap() falla con ENOMEM en un archivo disperso de 1TB?

Aquí hay un código de muestra que muestra el problema, ¿alguna pista?

#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/mman.h> 
#include <sys/types.h> 
#include <unistd.h> 

int main(int argc, char *argv[]) { 
    char * filename = argv[1]; 
    int fd; 
    off_t size = 1UL << 40; // 30 == 1GB, 40 == 1TB 

    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666); 
    ftruncate(fd, size); 
    printf("Created %ld byte sparse file\n", size); 

    char * buffer = (char *)mmap(NULL, (size_t)size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 
    if (buffer == MAP_FAILED) { 
     perror("mmap"); 
     exit(1); 
    } 
    printf("Done mmap - returned 0x0%lx\n", (unsigned long)buffer); 

    strcpy(buffer, "cafebabe"); 
    printf("Wrote to start\n"); 

    strcpy(buffer + (size - 9), "deadbeef"); 
    printf("Wrote to end\n"); 

    if (munmap(buffer, (size_t)size) < 0) { 
     perror("munmap"); 
     exit(1); 
    } 
    close(fd); 

    return 0; 
} 
+0

Como punto de interés, su programa funciona para mí hasta un tamaño de 256 GB ('1 << 38 '), con algo más alto que devuelve' EINVAL'. Esto está en RHEL4 (kernel 2.6.9-42.0.3.ELsmp). – caf

+4

¿Qué dice ulimit -a? – bmargulies

+0

Gracias, bmargulies, eso fue todo. ulimit: memoria virtual informada como 1804800 kbytes (poco más de 1,7 GB). ulimit -v 1610612736 (1.5TB) me permite mapear mi archivo disperso de 1TB. Responderé a mi propia pregunta para poder 'cerrarla' ... – metadaddy

Respuesta

12

El problema fue que el límite de memoria virtual por proceso se estableció en solo 1,7 GB. ulimit -v 1610612736 configúrelo en 1.5TB y mi llamada mmap() fue exitosa. Gracias, bmargulies, por la sugerencia de probar ulimit -a!

+2

Y, aparentemente, puedo establecer mi valor deseado (que puede ser 'ilimitado') en/etc/profile para hacerlo persistente. – metadaddy

2

¿Hay algún tipo de cuota por usuario, lo que limita la cantidad de memoria disponible para un proceso de usuario?

+0

Sí - Probé la sugerencia de bmargulies de probar ulimit -a y eso apuntó al límite del proceso de 'memoria virtual' como el culpable - vea mi respuesta a continuación. .. – metadaddy

1

Supongo que el núcleo está teniendo dificultades para asignar la memoria que necesita para mantenerse al día con esta asignación de memoria. No sé cómo se mantienen las páginas intercambiadas en el kernel de Linux (y supongo que la mayor parte del archivo estaría en estado de intercambio la mayor parte del tiempo), pero puede terminar necesitando una entrada para cada página de memoria que el archivo ocupa en una tabla. Dado que este archivo puede ser mmapped por más de un proceso, el kernel debe seguir el mapeo desde el punto de vista del proceso, que se correlacionaría con otro punto de vista, que se correlacionaría con el almacenamiento secundario (e incluiría campos para dispositivo y ubicación)

Esto encajaría en su espacio direccionable, pero podría no encajar (al menos contiguamente) en la memoria física.

Si alguien sabe más acerca de cómo Linux hace esto, estaría interesado en saberlo.

+4

Linux no creará las PTE (entradas de la tabla de páginas) hasta que esas páginas se toquen realmente. Todo lo que hace cuando crea la asignación es crear una sola estructura de VMA (área de memoria virtual), que básicamente contiene la información de 'mmap()'. – caf

+0

@caf: Gracias por la información – nategoose

Cuestiones relacionadas