ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Linux] mmap()
    LinuxProgramming 2023. 10. 15. 21:40

    "mmap() == 내 RAM에 파일을 매핑하기"

     

    기존의 file descriptor를 통한 파일작업 방식은 프로세스에서 파일을 읽을 때,

    OS의 시스템 콜을 시작으로 저장매체에 접근하여 파일을 읽기 까지의 과정이 복잡하며 오래걸린다. 

    더불어 내부적으로 OS가 처리해야하는 과정(시스템콜, 인터럽트, 스케줄링)이 많기 때문에 CPU의 성능이 떨어지기 마련이다. 

    이처럼 파일에 접근하는 과정의 효율성을 높이기 위해 나온 함수가 mmap()이다.  

     

    해당 파일을 읽기 위해서 저장매체에 접근하는 것이 아닌, 메모리의 데이터에 접근하는 방식인 것이다!

     

    특정 파일을 내 physical memory(RAM)의 virtual memory공간에 mapping 해줌으로써 

    기존에 직접 Device Storage까지 가서 파일작업을 하는 방식보다 훨씬 효율적이다.

     

    즉 이제 file descriptor를 쓰지 않고 mmap으로 받은 포인터변수만 사용해서 파일을 참조가능하다는 것이다.

    그것도 훨씬 빠르고 효율적인 방법으로 말이다.

     


    [ mmap() vs open() ]

     

    mmap() 시 사용하는 physical memory 내 가상메모리 공간은 다음과 같다.

     

    기존 open() system call로 얻은 fd를 가지고 read() write() 작업을 할때는 직접 Device Storage를 건들면서 작업하는 것이었다.

     

    하지만 이제는 해당 file을 내 RAM에 mmaping했으므로 RAM내에서 작업이 가능하다.

    구지 Device Storage를 건들지 않아도 된다.


    [mmap 방식]

    오직 두 가지 뿐이다. MAP_SHARED 아님 MAP_PRIVATE 이다.

    MAP_SHARED는 공유메모리처럼 타 프로세스와 공유할 수 있는 방식으로 매핑하는 것이고

    MAP_PRIVATE은 자신의 지역변수로 매핑하는 것이다. 


    [ mmap()의 인자 ] 

    #include <sys/mman.h>
    
    mmap(void* addr, size_t length,
    	int prot, int flags,
    	int fd, off_t offset);

     

    1) addr : 그냥 NULL 집어넣는다고 알면 된다. 99% NULL이다. 내가 직접 지정할 상황은 거의 없다.

    2) length : 할당할 메모리 크기

    struct stat 구조체에 fd나 filename을 이용하여 파일 정보를 저장하고 st_size를 집어넣어주면 된다.

     

    3) prot : 권한설정(PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE=접근불가)

    4) flags : 매핑종류(MAP_PRIVATE, MAP_SHARED, MAP_ANONYMOUS)

    MAP_PRIVATE : 프로세스 내부에 생성.

    MAP_SHARED : 프로세스 외부에 생성.

    MAP_FIXED : 지정된 공간에 생성

    MAP_ANONYMOUS : fd를 무시한다. 특정 파일을 매핑하는게 아닐때 사용한다

     

    5) fd : 어떤 파일을 매핑시킬 것인지

    mmap을 사용하기 전에 원하는 파일을 open하여 fd값을 얻은 후 mmap의 5번째 인자로 넣어주어야한다.

    mmap 후에는 close(fd)로 해당 file descriptor를 닫아줘도 된다.

    만약 내가 특정 파일을 매핑하는 것이 아니라, 그냥 메모리공간만 필요하면 MAP_ANONYMOUS를 flag값으로 주고 fd값을 -1로 넣어줘도 된다.

     

    6) offset : 마지막 인자는 무조건 0 또는 PAGE_SIZE의 배수만 가능

    마지막 인자는 페이지 단위만 가능함.

    만약 10 30 이런거 들어가면 매핑 오류가 발생함.

    제대로된 주소가 들어가지 않았다는거임. 그래서 segmentation fault가 발생함.

    segfault는 보통 주소 오류임

     


    [ mmap() 작동 원리 ] 

    이 과정을 이해하기 위해서는 기본적으로 운영체제와 가상 메모리에 대한 이해가 필요하다. 

     

    1. mmap() 실행 후, RAM의 가상 메모리 주소에 file 주소가 매핑된다. (현재 사용되고 있는 페이지 테이블에 할당된다.)

    2. 해당 메모리에 접근할 때, OS가 파일 데이터를 복사하여 물리 메모리(RAM)에 업로드 된다.

    이게 OS입장에서 개이득인게 파일에 접근하려고 저장장치(Disk/HDD/SSD)까지 가지 않고 그냥 옆에 있는 RAM을 통해 접근할 수 있기 때문이다.

    3. read - 가상 메모리에 매핑되어 있는 물리 메모리(RAM)의 데이터를 읽는다. (메모리 접근만 실행) 

    4. write - 해당 물리 메모리의 데이터를 수정한 후, 가상 메모리에 매핑된 페이지의 상태를 나타내는 flag를 수정한다.

     

    즉, 가상메모리의 페이징 시스템을 통해 파일 데이터를 조작 및 처리하여 저장매체(Device Storage)에 직접 접근하는 횟수를 최소화 한다. 

    이와 같은 이유로 파일 처리 과정의 성능 및 효율성이 개선된다. 

     


    [ munmap() ]

    int munmap(void *addr, size_t length);

     

    mmap()으로 매핑된 메모리 해제length로는 mmap 시 지정했던 길이가 들어간다.

    struct stat 구조체의 st_size 집어넣어주면 된다.

     


    [ mapping 된 메모리 동기화 ]

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

     

    addr로 시작하는 메모리 영역에서 [addr + length] 만큼의 내용을 백업 저장 장치로 기록한다.

    그래서 보통 그냥 전체 파일을 한다. 그게 가장 편함.

     

    msync(addr, statbuf.st_size, MS_SYNC);

     

     

    MS_ASYNC

    msync() 함수는 즉시 return한다. 그리고 synchronization 작업은 background process로 들어간다. 즉 synchronization하면

    서 진행중이던 프로세스(foreground process)를 진행할 수 있게 한다.

     

    MS_SYNC

    synchronizationd이 끝나기 전까지 return 값을 주지 않는다.

    즉 synchronization이 다 끝날때까지 프로세스는 멈춰있는다.

     


    [ 강의메모 ]

     

    1. 새 파일을 O_CREAT로 만들고 사용할땐 무조건 ftruncate 해줘야함

     

    open하면서 O_CREAT flag로 새로 만들면 파일크기가 0byte임.

    따라서 바로 mmap하면 0byte 파일을 매핑한거라 쓰려고 할때 bus 오류가 뜸.

     

    따라서 mmap하고 ftruncate를 해서 파일 크기를 늘려준 다음에 쓰기작업을 해야함

     

    ftruncate는 두번째 인자로 지정한 크기로 파일을 만듬.따라서 파일을 늘릴 수 도 있고 줄일 수 도 있음.

     

    늘리거나 줄일때는 모두 0으로 채움.

     

    (1) bus 오류 == 작업하려는데 파일크기가 0일때

    (2) segfault 오류 == 어떤 주소에 작업을 하려는데 그 주소가 오류이거나 내 공간의 주소가 아닐때

     

    2.

    open할때 O_RDONLY랑 O_WRONLY랑 mmap할때 쓴 PROT랑 짝이 맞아야함

    안전한건 애초에 open할때 O_RDWR로 open해주는거임.

     

    6번은 문제가 없다

     

    근데 6번은 O_RDONLY로 open했는데 만약 이걸 O_WRONLY로 하면 문제가 생긴다

     

    mmap 할때 O_WRONLY로 open하면 절대 안된다

     

    mmap 한다는 얘기는 말 자체가 데이터 파일을 읽어온다는거임.

    그래서 mmap 하려면 당연히 해당 파일에 대한 READ 퍼미션은 필수임.

     

    그래서 mmap할때는 open으로 file open할때 무조건 O_RDONLY 또는 O_RDWR 이 두개로만 오픈해야함.

    즉, WRITE만 할거여도 READ가 포함된 퍼미션으로 오픈해줘야함.

     

    O_WRONLY로 open하고 mmap 하면 mapping에 실패한다 == segmentation fault

     

    3. mmap은 file open과 mmap 호출할때만 조심하면 된다

     

    open할때 READ PERMISSION 무조건 있게 하고 PROT랑 짝맞춰서 하고

     

    file open

    1. mmap에 쓰는 작업일때 = O_RDWR
    2. mmap에서 읽는 작업일때 = O_RDONLY

    mmap

    1. mmap에 쓰는 작업일때 = PROT_WRITE
    2. mmap을 읽는 작업일때 = PROT_READ

     

    'LinuxProgramming' 카테고리의 다른 글

    [Linux] execl() and execv()  (1) 2023.10.18
    [Linux] wait() and waitpid()  (2) 2023.10.17
    [Linux] signal()  (1) 2023.10.16
    [Linux] pipe()  (0) 2023.10.16
    [Linux] FIFO  (0) 2023.10.14