본문 바로가기
컴퓨터공학기초 개념/시스템 프로그래밍

36. 시스템 프로그래밍 - mmap 사용방법

by devraphy 2021. 9. 27.

0. 시작하기전에

- 이전 포스팅에서 mmap을 어떻게 작동하는지 알아보았다.

- 이번 포스팅에서는 mmap을 직접 사용 및 활용해보자. 


1. mmap()의 반환값 

- 만약 mmap()을 실행하여 성공적으로 파일을 메모리에 매핑한다면, 해당 메모리의 주소값이 반환된다. 

- mmap()이 실패하는 경우에는 에러가 발생한다.

- 이를 통해, mmap()의 실행결과를 판단할 수 있다.

 


2. msync()

 

- 파일내용이 수정되면 파일이 종료될 때, 파일에 접근하여 변경된 내용을 파일에 덮어씌우는 방식으로 내용이 수정된다. 

- 이를 수행하는 함수가 msync()다.

- 다음 msync()의 원문을 살펴보자. 

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

▶ *start

   - mmap() 실행으로부터 반환받은 메모리의 시작주소 

 

 

▶ length 

   - 동기화 할 길이

   - 전체 파일을 덮어씌울 수도 있지만, 변경된 부분만 수정할 수 도 있다. 

   - 이 경우, 시작주소부터 동기화할 부분까지의 길이만을 입력한다. 

   - 그러나 일반적으로 mmap() 실행시 사용한 길이를 입력한다. 

 

 

▶ flags

   - 동기화 방식을 설정한다. 

   - MS_ASYNC(비동기 방식으로 동기화 / 동기화 명령을 하달하고 결과에 상관없이 다음 작업을 수행한다.)

   - MS_SYNC(동기적 방식으로 동기화 / 동기화 명령을 처리할 때까지 대기상태가 된다.)

   - MS_INVALID(현재 메모리 맵의 데이터를 무시하고, 기존의 파일 데이터로 다시 덮어씌운다.)


3. 실습예제 - 파일 읽기 

- 우선 매핑될 파일을 만들어주자. 

- link.txt라는 이름으로 만들어준다. 

Hello, world!

 

- 다음 mmap() 프로그램을 작성한다. 

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, const char *argv[]) {
        char *filepath = "link.txt"; // 매핑할 파일
        struct stat fileInfo; // 파일 정보를 담을 구조체
        int fd;
        fd = open(filepath, O_RDONLY, (mode_t)0600); // 파일 데이터를 fd에 담음
        if (fd < 0) { // 파일 읽기 실패시 에러
                printf("can't open file\n");
                exit(EXIT_FAILURE);
        }

        fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
        printf("File size is %ji\n", (intmax_t)fileInfo.st_size); // 파일 데이터의 크기를 출력
        char *map = mmap(0, fileInfo.st_size, PROT_READ, MAP_SHARED, fd, 0);
        // 파일을 메모리에 매핑
        // 파일 처음부터(0), 파일 사이즈만큼(st_size), 읽기만 가능(PROT_READ), 다른 프로세스 공유가능(SHARED)
        // 매핑할 파일(fd), offset(0)
        // 즉, 파일 전체를 매핑하라는 의미
        // mmap()이 성공하면, *map에는 매핑된 메모리 주소값이 들어간다.

        if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
                close(fd);
                perror("Error mmaping the file");
                exit(EXIT_FAILURE);
        }

        // no need of scheduling, just access memory
        for (off_t i = 0; i < fileInfo.st_size; i++) {
                printf("Found character %c at %ji\n", map[i], (intmax_t)i);
        } // 매핑된 파일의 데이터(문자열 = 배열)를 하나씩 읽어온다.

        if (munmap(map, fileInfo.st_size) == -1) { // 매핑 해제
                close(fd);
                perror("Error un-mmapping the file");
                exit(EXIT_FAILURE);
        }

        close(fd);
        return 0;
}

- 위의 사진은 실행 결과다. 

- 실행결과를 보면 총 13개의 데이터가 출력된 것을 확인할 수 있다.

- 마지막 데이터는 공백을 의미한다. 

- 각 데이터는 1바이트이며, 총 13바이트의 데이터 라는 것을 확인할 수 있다.

 


4. 실습예제 - 파일 쓰기

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

int main(int argc, const char *argv[]) {
        struct stat fileInfo;
        char *filepath = "link.txt"; // 매핑할 파일
        char *update = "hello, mmap!!!"; // 업데이트할 내용
        int fd = open(filepath, O_RDWR, (mode_t)0600); // 파일 데이터를 fd에 담음(읽기 쓰기 가능)
        if (fd == -1) { // 파일 읽기 실패시 에러
                printf("Error opening file for writing\n");
                exit(EXIT_FAILURE);
        }
        fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
        printf("%ld\n", fileInfo.st_size); // 파일 사이즈를 출력
        char *map = mmap(0, fileInfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        // 파일을 메모리에 매핑(읽기 쓰기 가능)

        if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
                close(fd);
                perror("Error mmaping the file");
                exit(EXIT_FAILURE);
        }

        printf("%ld", strlen(update)); // 업데이트할 데이터의 길이를 출력
        for(size_t i = 0; i < strlen(update); i++) {
                printf("Writing character %c at %zu\n", update[i], i);
                map[i] = update[i]; // 업데이트 할 문자열을 매핑된 데이터와 교체
        }

        if (msync(map, fileInfo.st_size, MS_SYNC) == -1) {
                perror("Could not sync the file to disk");
        }

        if (munmap(map, fileInfo.st_size) == -1) { // 매핑 해제
                close(fd);
                perror("Error un-mmapping the file");
                exit(EXIT_FAILURE);
        }

        close(fd);
        return 0;
}

 

- 성공적으로 파일이 업데이트된 것을 확인할 수 있다. 

 

 

a) 알아두자! 

- munmap()을 사용하지 않더라도, 자동으로 sync작업이 수행된다. 

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

int main(int argc, const char *argv[]) {
        struct stat fileInfo;
        char *filepath = "link.txt"; // 매핑할 파일
        char *update = "No munmap!!!"; // 업데이트할 내용
        int fd = open(filepath, O_RDWR, (mode_t)0600); // 파일 데이터를 fd에 담음(읽기 쓰기 가능)
        if (fd == -1) { // 파일 읽기 실패시 에러
                printf("Error opening file for writing\n");
                exit(EXIT_FAILURE);
        }
        fstat(fd, &fileInfo); // fstat(): fd를 구조체에 넣음
        printf("%ld\n", fileInfo.st_size); // 파일 사이즈를 출력
        char *map = mmap(0, fileInfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        // 파일을 메모리에 매핑(읽기 쓰기 가능)

        if(map == MAP_FAILED) { // 매핑에 실패하여 에러메세지가 담긴 경우
                close(fd);
                perror("Error mmaping the file");
                exit(EXIT_FAILURE);
        }

        printf("%ld", strlen(update)); // 업데이트할 데이터의 길이를 출력
        for(size_t i = 0; i < strlen(update); i++) {
                printf("Writing character %c at %zu\n", update[i], i);
                map[i] = update[i]; // 업데이트 할 문자열을 매핑된 데이터와 교체
        }

        close(fd);
        return 0;
}

 - 위의 코드는 munmap()과 msync()없이 파일에 쓰기 작업을 실행한다. 

 

- 위의 결과처럼, munmap()과 msync()를 사용하지 않아도 작업을 완료하면 자동으로 동기화를 수행하는 것을 확인할 수 있다.

댓글