본문 바로가기
Linux System/Linux

[func] mmap() / munmap() - memory 맵핑

by rewyear 2021. 7. 23.

Definition

파일이나 장치를 메모리에 대응 or 해제 시킴

#include <unistd.h>
#include <sys/mman.h>

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

 

Description

mmap() 함수는 fd로 지정된 파일에서 offset을 기점으로 시작해서 length byte만큼 start 주소로 대응시키는 함수

offset과 length는 PAGE_SIZE의 단위여야 함(대개 start 주소를 0으로 지정)

지정된 영역이 mapping된 실제 시작위치를 반환

 

* PAGE_SIZE?
Page는 MMU에서 메모리 관리 시 사용하는 최소 단위

 

 

prot: 메모리 보호 모드 설정

  • PROT_EXEC: 페이지는 실행가능하다.
  • PROT_READ: 페이지는 읽을 수 있다.
  • PROT_WRITE: 페이지는 쓰여질 수 있다.
  • PROT_NONE: 페이지는 접근할 수 없다.

 

flag: 대응된 객체 타입, mapping 옵션, 공유 여부 등 설정

  • MAP_FIXED: 지정된 주소 이외의 다른 주소를 선택하지 않는다. 지정된 주소가 사용될 수 없다면 mmap()는 실패한다. 만일 MAP_FIXED가 지정되면, start는 페이지 크기의 배수이어야 한다. 이 옵션은 사용하지 않는 것이 좋다.
  • MAP_SHARED: 대응된 객체를 다른 모든 프로세스와 공유한다.
  • MAP_PRIVATE: 개별적인 copy-on-write(:12) 대응을 만든다.(다른 프로세스와 대응 영역을 공유하지 않는다).

 

Return 

mmap()

성공 시 mapping된 영역의 포인터를 반환

에러가 발생하면 MAP_FAILED(-1)이 반환되며, errno는 적당한 값으로 설정된다.

 

mummap()

성공 시 0 리턴, 실패시 -1 리턴

 

Error

  • EBADF: fd가 유효한 파일 기술자가 아니다.
  • EACCES: MAP_PRIVATE가 설정되어 있지만 fd가 읽을 수 있도록 열려 있지 않다. 또는 MAP_SHARED와 RPOT_WRITE가 설정되어 있지만 fd가 쓸 수 있도록 열려있지 않다.
  • EINVAL: start나 length나 offset이 적당하지 않다. 보통 너무 크거나 PAGESIZE 경계로 정렬되어 있지 않을 경우.
  • ETXTBUSY: MAP_DENYWRITE가 설정되어 있으나 fd로 지정된 객체가 쓰기 위해 열려있다.
  • EAGAIN: 파일이 잠겨 있다.
  • ENOMEM: 사용할 수 있는 메모리가 없다.

 

Example

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int fd;
    char *file = NULL;
    struct stat stat_buf;
    long page_size;
    int flag = PROT_WRITE | PROT_READ;

    if (argc < 2) {
        fprintf(stderr, "Usage: input\n");
        exit(1);
    }

    if ((fd = open(argv[1], O_RDWR|O_CREAT)) < 0) {
        perror("File Open Error");
        exit(1);
    }
   
	// fstat으로 가져온 파일의 상태 및 정보를 알수 있음
    if (fstat(fd, &stat_buf) < 0) { 
        perror("fstat error");
        exit(1);
    }
	printf("%s %ld bytes\n", argv[1], stat_buf.st_size);
    
    // sysconf함수를 통해 시스템의 page size 얻어옴
    page_size = sysconf(_SC_PAGESIZE);
    printf("Page Size : %ld\n",page_size);

    file = (char *) mmap(0, 40, flag, MAP_SHARED, fd, 0);
    if (file == -1) {
        perror("mmap error");
        exit(1);
    }
    
    printf("%s\n", file);
    memset(file, 0x00, 40);
    mnumap(file);
    close(fd);
}

 

장단점

장점

  • 메모리에 맵핑된 파일을 읽거나 쓰면 read() 나 write() 시스템 콜을 사용할 때 발생하는 불필요한 복사를 방지(추가적인 복사는 사용자 영역의 버퍼로 데이터를 읽고 써야 하기 때문에 발생)
  • Page Fault 가능성을 제외하면 메모리에 맵핑된 파일을 읽고 쓰는 데 다른 시스템 콜 호출이나 컨텍스트 스위칭 같은 오버헤드가 발생하지 않음.
  • 여러 개의 프로세스가 같은 객체를 메모리에 맵핑한다면 데이터는 모든 프로세스에서 공유가능 읽기전용이나 MAP_SHARED 모드로 쓰기가 가능하게 맵핑된 객체는 전체가 다 공유된다.
  • lseek() 같은 시스템 콜을 사용하지 않고도 간단한 포인터 조작만으로 맵핑 영역을 탐색

단점​

  • 메모리 맵핑은 항상 PAGE_SIZE의 정수배만 가능하다. 따라서 맵핑하려는 파일 크기가 페이지 크기의 정수배 차이 만큼의 공간이 낭비될 수 있음
  • 32bit 에서 다양한 크기의 수많은 맵핑을 사용하게 되면 주소 공간의 파편화를 초래
  • 메모리 맵핑과 관련 자료구조를 커널 내부에서 생성, 유지하는 데 오버헤드가 발생한다.
300x250

'Linux System > Linux' 카테고리의 다른 글

[cmd] sed  (0) 2022.03.15
Console, Terminal, TTY  (0) 2022.03.10
[cmd] install 명령어  (0) 2020.08.05
[func] poll  (0) 2020.04.07
[func] fcntl  (0) 2020.04.07