큰 파일 전체 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 test
그리고.file
테스트 결과: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
매핑에는 메모리 예약이 필요합니다.이러한 페이지에 기입하면, 카피 온 라이트 할당이 발생하는 일이 있습니다.즉, 물리 RAM + 스왑보다 더 큰 것을 매핑할 수 없습니다.를 사용해 보세요.MAP_SHARED
대신 매핑합니다.이는 매핑에 대한 쓰기가 디스크에 반영된다는 것을 의미합니다.따라서 커널은 라이트백을 통해 항상 메모리를 해방할 수 있기 때문에 제한은 없습니다.
또, 이 맵핑에 대해서PROT_WRITE
단, 메모리 매핑에서 읽습니다.또한 파일을 열었습니다.O_RDONLY
- 이 자체도 문제가 될 수 있습니다.O_RDWR
사용하고 싶다면PROT_WRITE
와 함께MAP_SHARED
.
에 대해서는PROT_WRITE
x86은 쓰기 전용 매핑을 지원하지 않지만 다른 플랫폼에서 seg fault를 일으킬 수 있기 때문에 x86에서만 이 문제가 발생합니다.부탁한다PROT_READ|PROT_WRITE
- 또는 읽기만 필요한 경우,PROT_READ
.
시스템(676MB RAM 탑재 VPS, 256MB 스왑)에서 문제를 재현했습니다.MAP_SHARED
결과적으로EPERM
error(로 열린 백업 파일에 쓸 수 없기 때문에)O_RDONLY
) 로의 변경PROT_READ
그리고.MAP_SHARED
그럼 매핑이 성공합니다.
파일의 바이트를 수정해야 하는 경우, 쓸 파일의 범위만 비공개로 하는 방법도 있습니다.그것은,munmap
으로 리매핑합니다.MAP_PRIVATE
쓰려는 영역을 지정합니다.물론 파일 전체에 쓰려면 8GB의 메모리가 필요합니다.
또는 다음과 같이 쓸 수 있습니다.1
이렇게 하면 매핑 요청이 성공할 수 있지만 실제로 8GB의 COW 메모리를 모두 사용하려고 하면 프로그램(또는 다른 프로그램!)이 OOM 킬러에 의해 중지됩니다.
Linux(및 기타 몇몇 UNIX 시스템)에는MAP_NORESERVE
mmap(2)의 플래그를 지정합니다.이 플래그는 스왑 영역의 오버커밋을 명시적으로 유효하게 하기 위해서 사용할 수 있습니다.이 기능은 시스템에서 사용 가능한 메모리 용량보다 큰 파일을 매핑할 때 유용합니다.
기능은 특히 이능은 this this this this this this this this this this this this this 와 함께 사용하면 편리합니다.MAP_PRIVATE
작은 에만 쓰려고 이 "Swap space reservation"을 반환하게 됩니다.그렇지 않으면 파일 전체의 스왑 공간 예약이 트리거되거나 시스템이 반환됩니다.ENOMEM
시스템 전체의 오버 커밋이 유효하게 되어 있지 않고, 시스템의 빈 메모리를 초과하는 경우).
해야 할 에 쓸 빈 킬러(Linux될 수 입니다. 시스템 상에서 스왑하여 최종적으로 OOM Killer(Linux)를 트리거하거나 응용 프로그램이 수신할 수 있다는 것입니다.SIGSEGV
.
가상 메모리가 부족하여 매핑을 처리할 수 없습니다.
예를 들어 8G RAM과 최대 8G 스왑(총 16G 가상 메모리 사용 가능)을 갖춘 머신이 있습니다.
최대 8G의 Virtual Box 스냅샷에서 코드를 실행하면 정상적으로 작동합니다.
$ 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
스왑을 해제하면 8G의 총 메모리가 남습니다.(액티브 서버에서 실행하지 마십시오.)결과는 다음과 같습니다.
$ sudo swapoff -a
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256
mmap: Cannot allocate memory
따라서 매핑을 저장하기에 충분한 가상 메모리가 있는지 확인하십시오(파일의 몇 페이지만 터치해도 상관없습니다).
언급URL : https://stackoverflow.com/questions/7222164/mmap-an-entire-large-file
'programing' 카테고리의 다른 글
sscanf("123456789123456789123456789123456789", "%d", &n")는 동작을 정의했습니까? (0) | 2022.07.27 |
---|---|
vuejs에서 다른 모달 표시 후 모달은 자동으로 숨깁니다. (0) | 2022.07.27 |
인터페이스 변수가 디폴트로는 스태틱하고 최종적인 이유는 무엇입니까? (0) | 2022.07.27 |
편백 컴포넌트 테스트 중에 vue 컴포넌트를 랩하려면 어떻게 해야 합니까? (0) | 2022.07.27 |
Firestore에서 문서가 삭제되지 않음 (0) | 2022.07.27 |