[System Call] 메모리 맵 - mmap(), munmap(), msync(), CreateFileMapping, MapViewOfFile, UnmapViewOfFile
출처 : http://no1rogue.blog.me/30097158983
* 메모리 맵 - mmap() <파일 혹은 디바이스를 메모리와 대응>
; 파일(리눅스에서는 디바이스도 파일로 처리하므로 디바이스도 메모리 맵으로 연결 가능)을 처리하기 위해서는 보통 저수준으로는 파일 디스크립터
* 특징
1. 생성된 메모리 맵을 포인터를 이용하여 쉽게 사용 가능하다.
2. 파일로 연결하여 사용시 메모리-파일 사이의 동기화가 편하다.
3. IPC(프로세스간 통신)로 활용 가능하다.
4. 대용량의 데이터를 사용할 시 성능이 향상된다.
* 주의점
- 메모리 맵은 바로 파일의 처리하는게 아니라 가상 메모리로 활용되는 페이지에 맵핑하는 방식이다. 그러므로 파일과 해당 메모리 맵이 된 페이지가 다른 공간이다. 그러므로 커널에 의해 여유 시간에 동기화(둘의 데이터가 같아지는..)가 될 때까지 서로 다른 데이터를 가질 수 있다. 그러므로 동기화에 대한 주의를 할 필요가 있다. <개발자가 직접 커널에 동기화를 명령 할 수 있는 함수도 있다. - fsync()>
IPC로 사용 할 때에도 프로세스간 동기화에 대한 주의도 필요하다.
* 2가지 방식
1. 공유 메모리 맵 방식 (Shred Memory-Map)
; 메모리 맵 변경 시 원본 파일과 데이터가 동기화가 되는 방식
2. 복사 메모리 맵 방식 (Private Memory-Map)
; 처음 메모리 맵에 매핑 될때 파일의 내용을 읽어와서 복사하고 그 이후 동기화 하지 않는 방식.
* 메모리 맵 생성 함수 - mmap()
void* mmap(void *state, size_t length, int prot, int flags, int fd, ott_t offset);
- void *state
; 할당받기 원하는 메모리 주소. 보통 0을 써서 커널이 적합한 공간을 임의로 할당해 주소를 받을 수 있고, 직접 입력하여 사용해도된다. 하지만 직접 입력하는 경우 해당 시스템의 페이지 (배수)단위로 주소 값을 설정해줘야 한다.
- size_t length
; 메모리 맵을 할 크기. 바이트 단위로 설정한다.
- int prot
; 메모리 보호 메커니즘. 플래그 형식이므로 비트 연산으로 복수 속성으로 지정 가능하다.
+ PROT_EXEC; 해당 페이지 실행 가능.
+ PROT_READ; 해당 페이지 읽기 가능.
+ PROT_WRITE; 해당 페이지 쓰기 가능.
+ PROT_NONE; 해당 페이지 접근 불가.
=> 매핑할 파일 디스크립터와 속성이 같아야한다.
- int flags
+ MAP_SHARED; 공유 메모리 맵 방식.
+ MAP_PRIVATE; 복사 메모리 맵 방식.
+ MAP_FIXED ; 메모리 시작 번지 지정시 사용.
=>MAP_SHARED/MAP_PRIVATE 둘 중에 한개만 지정해야된다.
- int fd
; 메모리 맵 방식을 사용할 파일 디스크립터.(파일 혹은 디바이스)
- ott_t offset
; 해당 파일 디스크립터에서 메모리 맵을 시작할 오프셋 값.
; 메모리 맵핑이 이루어진 가상 메모리 주소. 실패시 MAP_FAILED가 발생하고 errno에 해당 상황에 대한 값이 설정된다.
* 메모리 맵 해제 - munmap()
; 메모리 맵을 사용하고 자원을 해제 할 때 사용한다.
int munmap(void *start, size_t length);
- void *start
; 메모리 맵핑이 시작된 주소. mmap()의 반환 값을 넣으면된다.
- size_t length
; 메모리 맵을 한 길이. mmap() 사용시 size_t length 인자와 크기를 주면된다.
+ return value
;성공 0, 실패 -1
* 메모리 맵과 파일의 동기화 - fsync()
; 메모리 맵에 데이터를 갱신해도 바로 파일과 동기화가 이루어지는 것은 아니다. 커널이 여유 있을 때 동기화를 수행한다. 그러므로 개발자가 직접 동기화를 보장하고 싶을 때 사용한다. 그리고 munmap()을 하여 메모리 맵을 해제 할때에도 동기화를 해주면 데이터가 보장된다.
int msync(void *start, size_t length, int flags);
- void *start
; mmap()를 통해 리턴 받은 메모리 맵의 시작 주소.
- size_t length
; 동기화를 할 길이. 시작 주소로 부터 길이를 지정하면 된다.
- int flags
+ MS_ASYNC
; 비동기 방식. 동기화(Memory->File)하라는 명령만 내리고 결과에 관계 없이 바로 리턴.
그렇기 때문에 동기화가 된 건지 알수 없다.
+ MS_SYNC
; 동기 방식. 동기화(Memory->File)가 될때까지 블럭 상태로 대기한다.
+ MS_INVALIDATE
; 현재 메모리 맵을 무효화하고 파일의 데이터로 갱신. 즉 File->Memory
===================================================================================
...
#define MMAP_FULENAME "test_mmap"
#define MMAP_SIZE 64
...
int main()
{
int fd;
char *p_mmap; //메모리 맵으로 사용할 데이터 타입
...
fd = open(MMAP_FILENAME, O_RDWR|O_CREAT, 0664);
...
p_mmap = (char*)mmap((void*)0, MMAP_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED, fd, 0);
//공유 메모리 방식을 사용한다. 읽기/쓰기 가능
...
//memcpy(); 등으로 메모리 맵 데이터 갱신.
...
msync(p_mmap, MAP_SIZE, MS_SYNC);
...
munmap(p_mmap);
return 0;
}
=> 메모리 맵 할 파일 이름만 같으면 IPC도 된다.
------------------------------------------------------------
출처 : http://hsnote.tistory.com/141
윈도우의 경우는 아래의 자료를 참고.
[메모리 매핑파일]
HANDLE CreateFileMapping(HANDLE hFile, // (1)
LPSECURITY_ATTRIBUTES lpAttributes, //(2)
DWORD flProtect, // (3)
DWORD dwMaximumSizeHigh, // (4)
DWORD dwMaxImumSizeLow, // (5)
LPCTSTR lpName //(6)
);
위의 함수가 매모리매핑 파일을 만드는 함수이다..
위의 함수 사용시 매모리맵파일이 생성은 되지만 실제로 매모리에 연결되어 있진않다.
연결을 하려면 MapViewOfFile 이라는 함수를 써야지 그 때부터 메모리에 연결이 된다.
일단위 함수의 설명을 들어보자
목적 및 하는 일 : MMF라는 커널 객체를 만들고 그 핸들을 리턴한다.
메모리매핑파일을 만들수있는 기본정보들을 갖고 있는 커널객체를 만들고 그 핸들을 리턴한다.
인자설명 :
(1) hFile : CreateFile 함수로 해당파일을 열고 리턴받은 값.. (CreaqteFile 의 리턴값 파일핸들) 파일핸들을 인자로 넘겨준다.. , (어떤파일을 파일매핑할건지 정하는거다.)
(2) lpAttributes : 솔직히 잘 모르겠는데 .. 보통 이값은 NULL로 줬다. (아시는 분은 좀 알려주세여^^)
(3) flProtect : 읽기 전용으로 할건지 읽기 쓰기를 할건지 설정하는 옵션이다. ex) PAGE_READONLY, PAGE_READWRITE 등등
(3) dwMaximumSizeHigh : 매핑할 범위를 지정하는 상위 4바이트 (용량이 작은 범위라면 이곳에 0을 주고 그 다음 인자에 파일크기 및 원하는 범위를 지정한다.)
(4) dwMaximumSizeLow : 매핑할 범위 지정하는 하위 4바이트
(5) lpName : 파일매핑 객체이기 때문에 객체에 이름을 부여할때 쓴다. 보통 NULL을 주고 이름을 붙이도 다른곳에서 참조할때는 이름을 문자열로 이름을 지정한다.
사용예제 ) CreateFileMapping(hSource, NULL, PAGE_READONLY, 0, dwFileSize, NULL);
CreateFileMapping(hTarget, NULL, PAGE_READWRITE, 0, dwFileSize, NULL);
------------------------------------------------------------------------------------------------------------------------------
LPVOID MapViewOfFile(HANDLE hFileMappingObject, // (1)
DWORD dwDesiredAccess, // (2)
DWORD dwFileOffsetHigh, //(3)
DWORD dwFileOffsetLow, // (4)
SIZE_T dwNumberOfBytesToMap // (5)
);
목적 및 하는 일 : CreateFileMapping 함수로 만든 MMF 객체를 대상으로 실제 메모리번지에 연결을 하여 메모리 매핑파일을 만든다.
메모리 매핑파일 만들고 그 메모리의 시작번지를 리턴한다.
인자설명 :
(1) hFileMappingObject : CreateFileMapping 함수로 리턴받은 핸들값을 인자로 준다.
(2) dwDesireAccess : 접근권한을 설정하는 것으로 FILE_MAP_READ, FILE_MAP,WRITE 등으로 옵션을 준다.
(3) dwFileOffsetHigh : 메모리 주소가 연결될 크기 상위 4바이트
(4) dwFileOffsetLow : 메모리 주소가 연결될 크기 하위 4바이트
(5) dwNumberOfBytesToMap : 오프셋으로 부터 원하는 크기를 정한다.
사용예제 ) MapViewOfFile(hMapSource, FILE_MAP_READ, 0, dwAs, dwLen);
MapViewOfFile(hMapTarget, FILE_MAP_WRITE, 0, dwAs, dwLen);
------------------------------------------------------------------------------------------------------------------------------
해당함수들을 사용후에는 해제 해주어야 하는데
CreateFileMapping 함수 같은 경우 파일 핸들을 리턴함으로
CloseHandle(리턴값); 으로 해제하고
MapViewOfFile 함수 같은 경우는 메모리 주소를 리턴함으로
UnmapViewOfFile(리턴값); 으로 해제한다.
------------------------------------------------------------------------------------------------------------------------------