Mmap () un archivo grande completo

7 minutos de lectura

avatar de usuario
emergencia

Estoy tratando de “mapear” un archivo binario (~ 8 Gb) usando el siguiente código (test.c).

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define handle_error(msg) \
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
   const char *memblock;
   int fd;
   struct stat sb;

   fd = open(argv[1], O_RDONLY);
   fstat(fd, &sb);
   printf("Size: %lu\n", (uint64_t)sb.st_size);

   memblock = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
   if (memblock == MAP_FAILED) handle_error("mmap");

   for(uint64_t i = 0; i < 10; i++)
   {
     printf("[%lu]=%X ", i, memblock[i]);
   }
   printf("\n");
   return 0;
}

test.c se compila usando gcc -std=c99 test.c -o test y file de devoluciones de prueba: test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

Aunque esto funciona bien para archivos pequeños, obtengo una falla de segmentación cuando intento cargar uno grande. El programa en realidad devuelve:

Size: 8274324021 
mmap: Cannot allocate memory

Logré mapear todo el archivo usando boost::iostreams::mapped_file pero quiero hacerlo usando C y llamadas al sistema. ¿Qué está mal con mi código?

  • necesitas abrir el archivo con el O_LARGEFILE bandera. consultar manuales. no estoy seguro si los archivos >4GB pueden ser mmapedición

    – fóxis

    28 de agosto de 2011 a las 16:35

  • No se puede reproducir aquí. Su código funciona bien en un archivo 9G. ¿Cuánto (RAM+SWAP) tienes? ¿Cuál es su política actual de /proc/sys/vm/overcommit_memory?

    – Estera

    28 de agosto de 2011 a las 16:42

  • @Mat $free -m #Mem{ total: 1984, used: 1923, free 60} Swap{ total: 2021, used: 0, free: 2021} $ cat /proc/sys/vm/overcommit_memory #returns 0

    – Emer

    28 de agosto de 2011 a las 16:52


  • @phoxis se requeriría esa bandera de una máquina de 32 bits. Árbitro

    – Emer

    28/08/2011 a las 17:00

  • @Emer: lo siento, acabo de notar el x86_64

    – fóxis

    28 de agosto de 2011 a las 17:01

avatar de usuario
bdonlan

MAP_PRIVATE las asignaciones requieren una reserva de memoria, ya que escribir en estas páginas puede generar asignaciones de copia en escritura. Esto significa que no puede mapear algo mucho más grande que su ram + swap físico. Intenta usar un MAP_SHARED mapeo en su lugar. Esto significa que las escrituras en la asignación se reflejarán en el disco; como tal, el kernel sabe que siempre puede liberar memoria al hacer una escritura diferida, por lo que no lo limitará.

También observo que estás mapeando con PROT_WRITE, pero luego continúa y lee desde el mapeo de memoria. También abriste el archivo con O_RDONLY – esto en sí mismo puede ser otro problema para usted; debes especificar O_RDWR si quieres usar PROT_WRITE con MAP_SHARED.

Como para PROT_WRITE solo que esto funciona en x86, porque x86 no admite asignaciones de solo escritura, pero puede causar fallas de segmentación en otras plataformas. Solicitud PROT_READ|PROT_WRITE – o, si solo necesitas leer, PROT_READ.

En mi sistema (VPS con 676 MB de RAM, 256 MB de intercambio), reproduje su problema; cambiando a MAP_SHARED resulta en un EPERM error (ya que no puedo escribir en el archivo de respaldo abierto con O_RDONLY). cambiando a PROT_READ y MAP_SHARED permite que la asignación tenga éxito.

Si necesita modificar bytes en el archivo, una opción sería hacer privados solo los rangos del archivo en el que va a escribir. Es decir, munmap y reasignar con MAP_PRIVATE las áreas en las que desea escribir. Por supuesto, si tiene la intención de escribir a la archivo completo entonces necesita 8 GB de memoria para hacerlo.

Alternativamente, puede escribir 1 para /proc/sys/vm/overcommit_memory. Esto permitirá que la solicitud de asignación tenga éxito; sin embargo, tenga en cuenta que si realmente intenta usar los 8 GB completos de memoria COW, su programa (¡o algún otro programa!) será eliminado por el asesino OOM.

  • hmm… Mat logró mapear 8*3G (23G) con 16G de espacio virtual. Así que realmente no reserva memoria …

    –Karoly Horvath

    28 de agosto de 2011 a las 17:18


  • @yi_H, el kernel de Linux de forma predeterminada le permite superar el intercambio físico + en un cierto porcentaje: opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html Sin embargo, dependiendo de la configuración de su sistema, es posible que tenga las cosas configuradas en una configuración más estricta. O si solo tiene, digamos, 4G total, puede exceder el límite.

    – bdonlan

    28 de agosto de 2011 a las 17:21

  • Soy consciente de eso, mira lo que escribió.

    –Karoly Horvath

    28 de agosto de 2011 a las 17:24

  • Sí, noté O_RDONLY y PROT_WRITE. Sin embargo, necesito intercambiar bytes del archivo asignado sin modificarlo. Probé con PROT_READ y MAP_PRIVATE y no funcionó. Gracias por la respuesta

    – Emer

    28 de agosto de 2011 a las 17:33

  • @Emer, mira mi edición. Si necesita intercambiar 8 GB de archivo, necesitará 8 GB de memoria + intercambio para almacenar el resultado.

    – bdonlan

    28 de agosto de 2011 a las 17:36

Linux (y aparentemente algunos otros sistemas UNIX) tienen la MAP_NORESERVE bandera para mmap(2), que se puede usar para habilitar explícitamente la sobreasignación de espacio de intercambio. Esto puede ser útil cuando desea asignar un archivo más grande que la cantidad de memoria libre disponible en su sistema.

Esto es particularmente útil cuando se usa con MAP_PRIVATE y solo tiene la intención de escribir en una pequeña parte del rango asignado a la memoria, ya que de lo contrario activaría la reserva de espacio de intercambio de todo el archivo (o haría que el sistema regresara ENOMEMsi no se ha habilitado la sobreasignación en todo el sistema y excede la memoria libre del sistema).

El problema a tener en cuenta es que si escribe en una gran parte de esta memoria, la reserva de espacio de intercambio diferido puede hacer que su aplicación consuma toda la memoria RAM libre e intercambie en el sistema, lo que eventualmente activará el asesino OOM (Linux) o haciendo que su aplicación reciba un SIGSEGV.

No tiene suficiente memoria virtual para manejar esa asignación.

Como ejemplo, tengo una máquina aquí con 8G de RAM y ~8G de intercambio (por lo que hay 16G de memoria virtual total disponible).

Si ejecuto su código en una instantánea de VirtualBox que es ~ 8G, funciona bien:

$ ls -lh /media/vms/.../snap.vdi
-rw------- 1 me users 9.2G Aug  6 16:02 /media/vms/.../snap.vdi
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
[0]=3C [1]=3C [2]=3C [3]=20 [4]=4F [5]=72 [6]=61 [7]=63 [8]=6C [9]=65 

Ahora, si dejo el intercambio, me quedo con 8G de memoria total. (no ejecute esto en un servidor activo). Y el resultado es:

$ sudo swapoff -a
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
mmap: Cannot allocate memory

Así que asegúrese de tener suficiente memoria virtual para mantener esa asignación (incluso si solo toca algunas páginas en ese archivo).

  • ¿Puede verificar si puede asignar dos archivos 8G con un espacio virtual 8+8? suena un poco extraño que necesite el espacio a pesar de que es posible que algunas páginas nunca se carguen o modifiquen (en cuyo caso, simplemente se pueden eliminar).

    –Karoly Horvath

    28 de agosto de 2011 a las 17:01


  • yo puedo mmap muchos archivos de menos de 16G con (8G+8G) memoria virtual disponible (aumentó a ~ 23G con tres archivos). No puedo mapear un solo archivo >8G con solo el 8G.

    – Estera

    28 de agosto de 2011 a las 17:11


  • Gracias por probarlo. ¿Hay alguna manera de calcular la cantidad exacta de memoria virtual necesaria para mapear un archivo? o dependeria de muchos factores?

    – Emer

    28 de agosto de 2011 a las 17:36

  • @Mat, 0 no es ‘siempre sobrecompromiso’; 1 es

    – bdonlan

    28 de agosto de 2011 a las 17:52

  • @bdonlan: tienes razón, lo siento, todo el comentario estuvo mal

    – Estera

    28 de agosto de 2011 a las 17:57

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad