메모리 매핑은 파일을 프로세스의 메모리에 매핑하는 것
즉, 프로세스에 전달한 데이터를 저장한 파일을 직접 프로세스의 가상 주소 공간(논리 주소)으로 매핑하는 것
따라서 read나 write함수를 사용하지 않고도 프로그램 내부에서 정의한 변수를 사용해 파일에서 데이터를 읽거나 쓸 수 있다.
파일을 프로세스의 가상 메모리에 매핑
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off);
return 성공시 매핑된 메모리의 시작 주소(매핑된 영역의 크기는 시작주소+len), 실패시 상수 MAP_FAILED(<sys/mman.h>에 정의)
addr: 매핑할 메모리 주소
len: 메모리 공간의 크기
prot: 보호 모드
flags: 매핑된 데이터의 처리 방법을 지정하는 상수
fd: file descripter
off: 파일 오프셋
- [prot]에 사용하는 값은 상수로 정의, OR 연산자로 연결해 지정 가능
PROT_READ: 매핑된 파일을 읽기만
PROT_WRITE: 매핑된 파일에 쓰기를 허용
PROT_EXEC: 매핑된 파일을 실행할 수 있다
PROT_NONE: 매핑된 파일에 접근할 수 없다- [flags] 상수로 정의, OR 연산자로 연결해 지정 가능
MAP_SHARED
다른 사용자와 데이터의 변경 내용을 공유한다. 이 플래그가 설정되어 있으면 쓰기 동작은 매핑된 메모리의 내용을 변경한다.
MAP_PRIVATE
데이터의 변경 내용을 공유하지 않는다. 이 플래그가 설정되어 있으면 최초의 쓰기 동작에서 매핑된 메모리의 사본을 생성하고 매핑 주소는 사본을 가리킨다. 따라서 MAP_SHARED를 지정한 다른 사용자는 내용을 변경하며 작업할 수 있습니다. MAP_SHARED와 MAP_PRIVATE 플래그 중 하나는 반드시 지정해야 한다.
MAP_FIXED
매핑할 주소를 정확히 지정한다. MAP_FIXED 플래그를 지정하고 mmap 함수가 성공하면 해당 메모리 영역의 내용은 매핑된 내용으로 변경된다. 해당 메모리 영역에 매핑된 파일이 있다면, 이 파일의 매핑은 해제되고 새 파일이 매핑된다.이 플래그는 시스템이 메모리를 효율적으로 사용할 수 없게 하므로 사용을 권장하지 않는다.
MAP_NORESERVE
MAP_PRIVATE를 지정하면 시스템은 매핑에 할당된 메모리 공간만큼 스왑 영역을 할당한다. 이 스왑 영역은 매핑된 데이터의 사본을 저장하는 데 사용한다.MAP_NORESERVE를 지정하면 매핑된 데이터를 복사해놓기 위한 스왑 영역을 할당하지 않는다. 따라서 복사된 데이터가 없으므로 매핑된 데이터를 수정하려고 하면 오류가 발생한다.
MAP_ANON
이 플래그가 설정되어 있고, 파일 기술자가 -1이면 mmap 함수는 익명의 메모리 영역 주소를 리턴한다. 이는 매핑할 파일로 /dev/zero를 지정하는 것과 동일하다.
/dev/null
이 파일에 쓰는 모든 것은 사라진다.
ex) 표준 출력이나 표준에러 막기(rm $badname 2>/dev/null)
파일 자체와 모든 퍼미션을 그대로 가지면서 내용만 지우기(cat /dev/null > /var/log/messages)
로그 파일의 내용을 자동으로 비우기(cat /dev/null > /var/log/wtmp)
/dev/zero
/dev/zero도 /dev/null 처럼 가상파일이지만, 실제로 널값을 가지고 있다.
이 파일에도 무언가를 쓰면 그 출력은 사라진다.
특정한 길이의 초기화된 더미 파일을 임시 스왑 파일로 만드는데 주로 쓰인다.
MAP_ALIGN
메모리 정렬(memory alignment)을 지정한다. addr 인자에 메모리 정렬값을 지정한다. 이 값은 0 또는 sysconf 함수에서 리턴한 페이지 크기의 배수여야 한다. 0을 지정하면 시스템이 적절한 값을 선택한다.
MAP_TEXT
이 플래그는 매핑된 메모리 영역을 명령을 실행하는 영역으로 사용할 것임을 시스템에 알린다. 예를 들어, 동적 라이브러리 등을 매핑할 수 있다.
MAP_INITDATA
매핑된 메모리 영역을 실행 파일을 위한 초기 데이터 영역으로 사용할 것임을 지정한다.
mmap 함수와 기존 방식 비교
먼저 파일 입출력 함수는 보통 다음과 같이 사용
fd = open(...); lseek(fd, offset, whence); read(fd, buf, len);
즉, 파일을 열고 필요에 따라 파일의 오프셋을 이동시키고 read 함수를 호출해 데이터를 buf로 읽어와서 작업
mmap 함수를 사용하는 경우fd = open(..); address = mmap((caddr_t) 0, len, (PROT_READ | PROT_WRITE), MAP-PRIVATE, fd, offset);
일단 파일을 여는 것은 같다. 이후 열린 파일의 내용을 mmap함수를 이용해 메모리에 매핑하고 이후의 작업은 address가 가리키는 메모리 영역의 데이터를 대상으로 수행, 매번 read 함수로 데이터를 읽어올 필요가 없음
int munmap(void *addr, size_t len);
return 성공시 0, 실패시 -1
addr이 가리키는 영역에 len 크기만큼 할당해 매핑한 메모리를 해제
즉, [addr+len] 크기 만큼의 메모리 영역을 해제
munmap 함수로 매핑을 해제한 메모리 영역에 접근하면 SIGBUS 또는 SIGSEGV 시그널이 발생
메모리로 매핑된 영역의 보호모드를 변경할 때 사용하는 함수
int mprotect(void *addr, size_t len, int prot);
return 성공시 0, 실패시 -1
매핑된 메모리의 보호모드는 mmap 함수로 메모리 매핑을 수행할 때 초깃값을 설정
mprotect함수는 addr로 지정한 주소에 len 크기만큼 매핑된 메모리의 보호모드를 prot에 지정한 값으로 변경(PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE)
존재하지 않거나 크기가 0인 파일은 mmap 함수를 사용해서 메모리에 매핑할 수 없음
int truncate(const char *path, off_t length);
int ftruncate(int fildes, off_t length);
path에 지정한 파일의 크기를 length로 지정한 크기로 변경
ftruncate 함수는 일반 파일과 공유 메모리에서만 사용가능
유닉스 시스템은 메모리에 매핑된 파일의 내용을 백업 공간에 복사해둔다. 따라서 매핑된 메모리의 내용과 백업 내용이 일치하도록 동기화해야한다. 메모리 매핑 기능을 사용해 매핑한 데이터를 부모 프로세스와 자식 프로세스가 공유하도록 할 수 있다.
int msync(void *addr, size_t len, int flags);
return 성공시 0, 실패시 -1
addr로 시작하는 메몸리 영역에서 [addr+len]만큼의 내용을 백업 저장 장치로 기록하도록 한다.
flags
MS_ASYNC
비동기 쓰기 작업을 수행한다. msync 함수는 즉시 리턴하고, 함수가 리턴한 후 적절한 시점에 쓰기 작업을 수행한다.
MS_SYNC
쓰기 작업을 완료할 때까지 msync 함수가 리턴하지 않는다. 메모리의 크기가 클 경우 비교적 시간이 걸릴 수 있다.
MS_INVALIDATE
메모리에 복사되어 있는 내용을 무효화한다.
프로세스가 mapping 된 메모리 주소 공간을 보여줌
모든 프로세스에는 각자 주소 공간이 있으며, 이 주소 공간은 가상 메모리 관리자가 제공하고 관리