서론


안녕하세요 이번 글에서는 mmap에 대해 알아보려고 합니다. 저는 이번에 mmap에 대해 배우기 전까지는 아예 mmap에 대해 알지 못했고, 사용해본적도 없는데 알고보니 상당히 중요한 내용이었습니다.

이 글을 읽는 분들도 이번 기회를 통해서 mmap에 대해 알게되면 좋겠습니다.

mmap이란?


mmap이란 memory mapping의 줄임말로, RAM의 특정 메모리 공간을 프로세스가 할당받아서 사용하는 것을 말합니다.

mmap은 두 가지 방식으로 동작하는데 각각 anonymous mappingfile mapping입니다.

file mapping을 하게되면 디스크에 있던 파일 데이터가 메모리에 올라간 후, 그 메모리 영역을 프로세스에 할당해주는 것 입니다.

그래서 이런 mapping은 메모리의 내용이 돌아갈 곳이 있다는 의미에서 backing store가 있다고 표현합니다.

anonymous mapping은 이와는 달리 단순히 메모리의 특정 공간을 0으로 초기화시킨 후 프로세스에 할당해주는 것입니다.

mmap()


이러한 mmap은 다음 함수의 호출을 통해서 이뤄지게 됩니다. addr로 시작하는 length만큼의 공간을 프로세스가 할당받게 되는 것이죠.

자세한 정보는 man-pages(mmap)에서 확인 가능합니다.

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

그리고 실제로 사용하는 예시는 다음과 같습니다.

file_memory = mmap(0, statbuf.st_size, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);

여기서 더 살펴볼 부분은 flags파트입니다. 이 flags 파트를 통해 이 mmap의 성격이 결정지어지게 됩니다. flags에 들어갈 수 있는 것들의 예시로 다음의 것들이 있습니다.

  • MAP_FILE
  • MAP_ANONYMOUS
  • MAP_PRIVATE
  • MAP_SHARE

위의 4개의 플래그 중 MAP_FILE, MAP_ANONYMOUS는 앞서 말씀드린 mmap의 두 가지 방식 중 하나를 고르는 플래그입니다.

그렇다면 MAP_PRIVATE, MAP_SHARE는 무엇일까요?

mmap을 하게되면 다른 프로세스가 그 mapping된 공간을 공유할 수 있습니다. 기본적으로 그렇게 하기위해서 MAP_SHARE나 MAP_PRIVATE를 사용하죠.

MAP_SHARE를 하게되면 서로 다른 프로세스들끼리 특정 메모리 영역을 공유하게 됩니다. 그리고 한 프로세스가 공유 메모리 영역을 변화시키면 다른 프로세스도 변화된 공유 메모리 영역을 보게됩니다.

MAP_PRIVATE를 쓰게되면 어느 프로세스가 공유 메모리 영역을 변화시키기전까지는 여러 프로세스들이 같은 메모리 영역을 보면서 공유할 수 있습니다.

그렇지만 어떤 프로세스가 공유 메모리 영역을 변화시키게되면 그 프로세스는 기존의 공유 메모리 영역을 카피한 후, 다른 곳에 복사해서 나가고, 기존의 공유 메모리 영역은 원본 상태를 유지하게 됩니다. 이를 Copy-on-write라고 합니다.

mmap 사용 이유


mmap에 대해 알아보았으니 그러면 이를 왜 쓰는가가 궁금해집니다. 저는 IBM doc(mmap)과 mmap을 기존 I/O와 비교하는 과정을 통해 이를 분석해보려고 합니다.

우선 IBM doc(mmap)을 참고해보면, read/write로 인한 부하를 줄이기 위해서 사용한다고 합니다. mmap이 동작하는 방식을 살펴보면 이를 이해할 수 있습니다.

파일을 읽어오는 상황을 가정해보죠. mmap을 알기 전에는 open()을 통해 커널 버퍼에 파일의 내용을 올려둔 뒤 read()를 통해 유저 버퍼로 내용을 가져왔을 것입니다.

즉 디스크에서 커널 버퍼로 복사 한번, 커널 버퍼에서 유저 버퍼로 복사 한번 총 두번의 복사가 일어나게 됩니다.

그렇지만 mmap을 활용하게 되면 이렇게 여러번 복사할 필요없이 파일 데이터를 메모리에 한번만 올려두면 다이렉트로 메모리에서 파일의 내용을 읽을 수 있습니다.

그리고 만약 여러 프로세스가 같은 파일의 내용을 읽어야하는 상황이라면 메모리에 한번만 파일이 올라가있기만 shared memory를 이용해서 공간을 효율적으로 사용하여 파일을 읽을 수 있곘죠.

fork()


mmap과 관련해서 재밌는 내용이 있습니다. fork()를 통해 자식 프로세스를 만들게되면 그 즉시 부모의 모든 것을 메모리에 복사해서 나갈 것이라고 생각하지만 실제로는 그렇게 작동하지 않는다고 합니다.

fork()를 한 후, exec으로 새로운 프로세스를 생성해버리면 기껏해둔 복사가 낭비된 것이기때문에 fork()를 하면 자식 프로세스에 readonly로 parent page에 대한 shared mapping을 생성한다고 합니다.

그 후 만약 자식 프로세스에서 쓰기 동작을 하면 protection fault가 나서 page 카피, page mapping 바꾸기를 한 후 다시 쓰기를 한다고 합니다.

마무리


이렇게 mmap에 대해 알아보았는데 이런 방식을 통해서 shared memory, 프로세스간 통신 등이 가능하다는 것을 알아가시는 기회가 되었으면 좋겠습니다.