c - mmap: no se puede asignar memoria
linux memory (1)
Tengo un programa C que calcula el tiempo de servicio de fallas de página en C. Para este programa tengo 2 archivos grandes (de menos de 3 GB cada uno, casi el tamaño de la RAM)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "rdstc.h"
#include "config.h"
#define KB 1024
#define MB 1024 * KB
#define GB 1024 * MB
#define SIZE_OF_MEMORY 1 * GB // Main memory size
#define handle_error(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)
int main(int argc, char *argv[]){
int fd1, fd2;
char *addr1, *addr2, c;
int i, j;
long long unsigned int s_t, e_t, t=0;
if (argc != 3){
printf("usage: a.out <file1> <file2> /n");
exit(EXIT_FAILURE);
}
if ((fd1 = open(argv[1], O_RDONLY)) == -1){
handle_error("open");
}
if ((fd2 = open(argv[2], O_RDONLY)) == -1){
handle_error("open");
}
posix_fadvise(fd1, 0, 0, POSIX_FADV_RANDOM);
posix_fadvise(fd2, 0, 0, POSIX_FADV_RANDOM);
addr1 = (char *) mmap(0, SIZE_OF_MEMORY, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd1, 0);
if (addr1 == MAP_FAILED){
handle_error("mmap");
}
addr2 = (char *) mmap(0, SIZE_OF_MEMORY, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd2, 0);
if (addr2 == MAP_FAILED){
handle_error("mmap");
}
madvise(addr1, 0, MADV_RANDOM);
madvise(addr2, 0, MADV_RANDOM);
j = 32; // default read ahead size if 256 blocks (assuming each block is of 512 bytes)
for(i = 0; i < ITERATIONS; i++){
s_t = rdtsc();
c = addr1[i + j*4*KB]; // read at multiple of page size, so every read causes a page fault
j *= 2;
e_t = rdtsc();
t += (e_t - s_t);
}
printf("Time required to service a page faut is %f /n", (t/ITERATIONS)/CPU_FREQ);
munmap(addr1, SIZE_OF_MEMORY);
munmap(addr2, SIZE_OF_MEMORY);
return 0;
}
Recibo las siguientes advertencias del compilador:
lmelvix@Melvix:~/projects/mem$ gcc mem1_4.c -lm
mem1_4.c: In function ‘main’:
mem1_4.c:11:17: warning: integer overflow in expression [-Woverflow]
#define MB 1024 * KB
^
mem1_4.c:12:19: note: in expansion of macro ‘MB’
#define GB 1024 * MB
^
mem1_4.c:13:28: note: in expansion of macro ‘GB’
#define SIZE_OF_MEMORY 2 * GB // Main memory size
^
mem1_4.c:40:30: note: in expansion of macro ‘SIZE_OF_MEMORY’
addr1 = (char *) mmap(0, SIZE_OF_MEMORY, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd1, 0);
^
mem1_4.c:11:17: warning: integer overflow in expression [-Woverflow]
#define MB 1024 * KB
^
mem1_4.c:12:19: note: in expansion of macro ‘MB’
#define GB 1024 * MB
^
mem1_4.c:13:28: note: in expansion of macro ‘GB’
#define SIZE_OF_MEMORY 2 * GB // Main memory size
^
mem1_4.c:44:30: note: in expansion of macro ‘SIZE_OF_MEMORY’
addr2 = (char *) mmap(0, SIZE_OF_MEMORY, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd2, 0);
^
mem1_4.c:11:17: warning: integer overflow in expression [-Woverflow]
#define MB 1024 * KB
^
mem1_4.c:12:19: note: in expansion of macro ‘MB’
#define GB 1024 * MB
^
mem1_4.c:13:28: note: in expansion of macro ‘GB’
#define SIZE_OF_MEMORY 2 * GB // Main memory size
^
mem1_4.c:62:19: note: in expansion of macro ‘SIZE_OF_MEMORY’
munmap(addr1, SIZE_OF_MEMORY);
^
mem1_4.c:11:17: warning: integer overflow in expression [-Woverflow]
#define MB 1024 * KB
^
mem1_4.c:12:19: note: in expansion of macro ‘MB’
#define GB 1024 * MB
^
mem1_4.c:13:28: note: in expansion of macro ‘GB’
#define SIZE_OF_MEMORY 2 * GB // Main memory size
^
mem1_4.c:63:19: note: in expansion of macro ‘SIZE_OF_MEMORY’
munmap(addr2, SIZE_OF_MEMORY);
^
Cuando lo ejecuto con el comando obtengo el error
./a.out file1.txt file2.txt
mmap: Cannot allocate memory
¿Qué hace el código? Mapeamos ambos archivos usando banderas
MAP_PRIVATE (para que ningún otro proceso tenga acceso a este archivo) y MAP_POPULATE (para que
cuando llamamos a mmap (), el archivo completo se mapea en la memoria) junto con el indicador de protección PROT_READ.
Primero mapeamos el archivo 1 y como utilizamos MAP_POPULATE, la RAM completa se llena con los datos correspondientes a este archivo. Después de esto mapeamos el archivo2 usando los mismos indicadores y por lo tanto ahora tenemos el archivo2 completamente mapeado en la RAM. Por lo tanto, acceder a los datos del archivo 1 provocará fallas en la página ya que el archivo2 ocupa toda la memoria RAM disponible. También llamamos a syscall madvise () con el indicador MADV_RANDOM establecido, para avisar al kernel que no lea las páginas en lectura, para ambos archivos. Entonces, una vez que esta configuración inicial se realiza con el archivo2 ocupando toda la RAM disponible, accedemos a los datos correspondientes al archivo1 aleatoriamente (para evitar cualquier efecto de optimización de lectura por lectura realizada por kernel y también evitamos leer de la memoria caché L3), ya que la RAM está llena de datos correspondientes al archivo2, cada acceso a los datos correspondientes al archivo provocará un error de página. Realizamos 10 lecturas aleatorias en la región mapeada en bucle y medimos el tiempo medio requerido para esta operación.
Echa un vistazo a la advertencia del compilador que obtienes. Aquí tienes un desbordamiento de enteros: #define SIZE_OF_MEMORY 2 * GB
. Eso equivale a 2^31
== 0b1000 ... 0
que para int firmado es igual a INT_MIN. Es por eso que mmap
falla.
Debe usar literales sin firmar en sus definiciones:
#define KB (1024u)
#define MB (1024u * KB)
#define GB (1024u * MB)
#define SIZE_OF_MEMORY (2u * GB)