Programing

전체 큰 파일을 Mmap ()

crosscheck 2020. 11. 21. 15:40
반응형

전체 큰 파일을 Mmap ()


다음 코드 (test.c)를 사용하여 바이너리 파일 (~ 8Gb)을 "mmap"하려고합니다.

#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는 gcc -std=c99 test.c -o testfile테스트 결과를 사용하여 컴파일됩니다 .test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

작은 파일에서는 잘 작동하지만 큰 파일을로드하려고하면 세그멘테이션 오류가 발생합니다. 프로그램은 실제로 다음을 반환합니다.

Size: 8274324021 
mmap: Cannot allocate memory

boost :: iostreams :: mapped_file을 사용하여 전체 파일을 매핑했지만 C 및 시스템 호출을 사용하여 수행하고 싶습니다. 내 코드에 어떤 문제가 있습니까?


MAP_PRIVATE이러한 페이지에 쓰면 쓰기 중 복사 할당이 발생할 수 있으므로 매핑에는 메모리 예약이 필요합니다. 이것은 물리적 램 + 스왑보다 너무 큰 것을 매핑 할 수 없음을 의미합니다. MAP_SHARED대신 매핑을 사용해보십시오 . 이것은 매핑에 대한 쓰기가 디스크에 반영된다는 것을 의미합니다. 따라서 커널은 쓰기 저장을 수행하여 항상 메모리를 확보 할 수 있다는 것을 알고 있으므로 제한하지 않습니다.

또한를 사용하여 매핑하고 PROT_WRITE있지만 계속해서 메모리 매핑에서 읽습니다. 또한로 파일을 열었습니다. O_RDONLY이 자체가 다른 문제 일 수 있습니다. 와 함께 O_RDWR사용 하려면 지정해야합니다 .PROT_WRITEMAP_SHARED

에 관해서는 PROT_WRITE86 쓰기 전용 매핑을 지원하지 않지만 다른 플랫폼에서 세그먼테이션 폴트 (segfault)을 일으킬 수 있기 때문 만이, 86에 일에 발생합니다. 요청 PROT_READ|PROT_WRITE-또는 읽기만 필요한 경우 PROT_READ.

내 시스템 (676MB RAM, 256MB 스왑이있는 VPS)에서 문제를 재현했습니다. 로 변경 MAP_SHARED하면 EPERM오류가 발생합니다 (로 연 백업 파일에 쓸 수 없기 때문에 O_RDONLY). 로 변경 PROT_READ하고하는 것은 MAP_SHARED매핑이 성공 할 수 있습니다.

파일의 바이트를 수정해야하는 경우 한 가지 옵션은 쓰려는 파일의 범위 만 비공개로 만드는 것입니다. 즉, 쓰려는 영역으로 munmap다시 매핑 MAP_PRIVATE하십시오. 물론 전체 파일 에 쓰려면 8GB의 메모리가 필요합니다.

Alternately, you can write 1 to /proc/sys/vm/overcommit_memory. This will allow the mapping request to succeed; however, keep in mind that if you actually try to use the full 8GB of COW memory, your program (or some other program!) will be killed by the OOM killer.


You don't have enough virtual memory to handle that mapping.

As an example, I have a machine here with 8G RAM, and ~8G swap (so 16G total virtual memory available).

If I run your code on a VirtualBox snapshot that is ~8G, it works fine:

$ 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 

Now, if I drop the swap, I'm left with 8G total memory. (Don't run this on an active server.) And the result is:

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

So make sure you have enough virtual memory to hold that mapping (even if you only touch a few pages in that file).


Linux (and apparently a few other UNIX systems) have the MAP_NORESERVE flag for mmap(2), which can be used to explicitly enable swap space overcommitting. This can be useful when you wish to map a file larger than the amount of free memory available on your system.

This is particularly handy when used with MAP_PRIVATE and only intend to write to a small portion of the memory mapped range, since this would otherwise trigger swap space reservation of the entire file (or cause the system to return ENOMEM, if system wide overcommitting hasn't been enabled and you exceed the free memory of the system).

The issue to watch out for is that if you do write to a large portion of this memory, the lazy swap space reservation may cause your application to consume all the free RAM and swap on the system, eventually triggering the OOM killer (Linux) or causing your app to receive a SIGSEGV.

참고URL : https://stackoverflow.com/questions/7222164/mmap-an-entire-large-file

반응형