728x90

3. Memory mapped I/O
 

#include <sys/mman.h>

void * mmap(void* addr,//힌트일 뿐이며 보통 NULL을 넘긴다.

size_t len,

int prot,//접근권한

int flags,//추가적인 동작 방식

int fd,//대상

off_t offset);//시작위치

prot 옵션

PROT_NONE 권한없음

PROT_READ 읽기 가능한 페이지

PROT_WRITE 쓰기 가능한 페이지

PROT_EXEC 실행 가능한 페이지

flags 옵션

MAP_FIXED addr을 요구사항으로 취급하다록 지시. 해당주소에 mapping하지 못하면 호출실패

프로세스 주소공간에 대한 상세한 지식을 요구, 호환성이 떨어짐.

MAP_PRIVATE mapping을 공유하지 않음. 파일은 write후 복사로 mapping되며 변경된 메모리 속성은 실제 파일이나 다른 프로세스의 map에는 반영되지 않는다.

MAP_SHARED 동일파일을 mapping한 모든 프로세스들이 mapping을 공유.

read시 다른 프로세스가 write한 내용도 반영

mapping은 page단위로 구성됨 -> addr과 offset은 반드시 page size의 정수배가 되어야 함

ex)

void *p;

p = mmap (0, len, PROT_READ, MAP_SHARED, fd, 0);

if(p == MAP_FAILED) //ERROR

#include <unistd.h>

long sysconf(int name); // page size를 얻기위한 표준 method(POSIX)

int getpagesize(void); // page size를 얻기위한 표준 method(linux)

<asm/page.h>

PAGE_SIZE //정의된 정적 매크로, 실행시점이 아니라 컴파일 시점에서

시스템 페이지 크기를 조회

#include <sys/mman.h>

int munmap (void *addr, size_t len);

mmap()으로 만들어진 mapping 제거

ex)

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcn시.h>

#include <unistd.h>

#include <sys/mman.h>

struct stat sb;

off_t len;

char *p;

int fd;

fd = open (filename, O_RDONLY);

if(fstat(fd, &sb) == -1) // ERROR

if(!S_ISREG (sb.st_mode)) //ERROR

p = mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);

if(p == MAP_FAILED) //ERROR

if(close (fd) == -1)//ERROR

for(len = 0; len < sb.st_size; len++)

putchar(p[len]);

if(munmap(p, sb.st_size) == -1) //ERROR



장점

- 사용이 편리(다른 작업없이 메모리에 접근하기만 하면 된다. 포인터로 접근)

- 메모리 절약(추가적인 버퍼 메모리가 필요 없다.)

- 속도가 빠르다. (파일이 클수록, OS영역에 캐싱되어 있을수록 속도차이가 난다.)

- 다중 프로세스가 동일 객체를 메모리에 mapping할 때, 모든 프로세스가 자료를 공유한다

단점

- memory와 I/O가 주소공간을 공유(사용 가능한 주소 공간이 제한됨)

- page size의 정수배만 가능하다.(여분의 공간이 낭비됨)

- 연속적인 공간이 있어야 하므로 크기가 다른 mapping이 많으면 단편화 때문에 적당한 공간을 찾지 못할 수도 있다.(32bit일때. 64bit 주소공간에서는 크게 문제되지 않는다)

추가

- 파일이 모두 메모리에 올라오는 것이 아니라 메모리 access시 메모리를 할당해주고 I/O가 발생
 

mmap size 조정 // 리눅스 전용

#define _GNU_SOURCE

#include <unistd.h>

#include <sys/mman.h>

void * mremap (void *addr, size_t old_size,

size_t new_size, unsigned long flags);

접근 권한 변경

#include <sys/mman.h>

int mprotect (const void *addr, size_t len, int prot);

성공하면 0을 return.

mapping으로 파일 동기화 // fsync()와 유사

#incldue <sys/mman.h>

int msync (void *addr, size_t len, int flags);

flags 설정(OR연산 가능)

MS_ASYNC 동기화가 비동기식으로 일어나야 함

MS_INVALIDATE 캐시된 복사본이 유효하지 않음을 명세, 향후 이 파일의 map에 접근할 경우 새롭게 동기화된 디스크 내용을 반영

MS_SYNC 동기화가 동기식으로 일어나야 함,

msync() 호출은 모든 페이지를 드스크에 다시 쓰기 전까지 결과를 반환하지 않음.


madvise

memorymap에 포함된 page와 관련된 동작방식을 조언. 리눅스 커널은 기본적으로 동적으로 동작방식을 조율하며 성능을 최적화하지만 madvise는 부하상황에서 바람직한 캐시와 미리읽기 동작방식을 보장한다.

#include <sys/mman.h>

int madvise (void *addr, size_t len, int advice);

advice 설정(POSIX에서 정의한 의미)

MADV_NORMAL 특별한 조언을 하지 않는다.

MADV_RANDOM page에 random 순서로 접근하려고 함

MADV_SEQUENTIAL 낮은 주소에서 높은 주소의 순서대로 페이지 접근

MADV_WILLNEED 당분간은 지정된 영역에 있는 페이지에 접근하려고 함

MADV_DONTNEED 당분간은 지정된 영역에 있는 페이지에 접근하지 않음

advice 옵션(linux 2.6 kernel에서의 동작)

MADV_NORMAL 커널이 적절히 미리읽기 작업을 수행(평상시와 동일)

MADV_RANDOM 커널이 미리읽기를 비활성화함, 매번 물리적인 연산이 일어날 때마다 최소 자료만 읽음

MADV_SEQUENTIAL 커널이 공격적인(적극적인) 미리읽기 작업을 수행

MADV_WILLNEED 커널이 미리읽기 작업을 시작해서 페이지를 메모리로 읽어 들인다.

MADV_DONTNEED 커널이 페이지와 관련된 자원을 해제하고, 변경되었지만 아직 동기화되지 않은 페이지는 버린다. 이후 mapping된 자료에 대한 접근이 있으면 파일에서 페이지를 읽어들인다.

728x90

+ Recent posts