903 words
5 minutes
260308_mmap

link#


1. mmap 함수 원형#

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

2. 주요 매개변수 및 설정#

3. mmap 사용 절차#

  • 파일 열기: fd = open("file", O_RDWR);
  • 메모리 매핑: addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  • 파일 조작: 포인터를 사용하여 배열처럼 데이터에 접근.
  • 동기화 (선택): msync(addr, size, MS_SYNC); (메모리 내용을 디스크에 즉시 반영).
  • 매핑 해제 및 종료: munmap(addr, size);close(fd)

4. C 코드 예제#

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

int main() {
    int fd = open("test.txt", O_RDWR | O_CREAT, 0664);
    size_t size = 1024;
    ftruncate(fd, size); // 파일 크기 설정 [1]

    // 1. mmap으로 파일 매핑
    char *map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (map == MAP_FAILED)
        return -1;

    // 2. 포인터로 파일에 쓰기
    sprintf(map, "Hello, mmap!");

    // 3. 매핑 해제 및 파일 닫기
    munmap(map, size);
    close(fd);
    return 0;
}

5. 특징 및 주의사항#

  • 성능: 대용량 파일 처리 시 read/write 시스템 콜 비용을 줄여 성능 향상.
  • 주의: 매핑 후 파일 크기를 변경하면 예기치 않은 오류 발생 가능.
  • 동기화: MAP_SHARED 사용 시에도 실제 파일 반영은 비동기적일 수 있으므로 필요시 msync 호출.
  • 페이지 단위: 매핑은 페이지 단위(getpagesize())로 이루어짐.
  • 메모리 고정: 필요시 mlock()을 통해 페이징 금지 가능

메모리 대응 입출력을 이용한 파일 복사 프로그램 예시#

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

int main(int argc, char *argv[]) {
    if (argc < 3) {
        fprintf(stderr, "Usage: %s <source_file> <dest_file>\n", argv[0]);
        exit(1);
    }
    int srcfd, dstfd; // src 파일 서술자, dst 파일 서술자
    void *src, *dst;  // src 메모리 주소, dst 메모리 주소
    size_t copysz;    // 다음 copy할  메모리 내용 size
    struct stat sbuf;
    off_t fsz = 0;  // 다음 읽기, 쓰기를 기록할 위치(offset)
    long page_size; // 시스템의 PAGE SIZE

    if ((srcfd = open(argv[1], O_RDONLY)) < 0) {
        fprintf(stderr, "can't open %s for reading \n", argv[1]);
        exit(1);
    }

    if ((dstfd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0777)) < 0) {
        fprintf(stderr, "can't open %s for writing\n", argv[2]);
        exit(1);
    }

    // file 사이즈 얻기 위한 용도
    if (fstat(srcfd, &sbuf) < 0) {
        fprintf(stderr, "fstat error\n");
        exit(1);
    }

    if (ftruncate(dstfd, sbuf.st_size) < 0) {
        fprintf(stderr, "ftruncate error\n");
        exit(1);
    }

    page_size = sysconf(_SC_PAGESIZE);
    printf("page_size : %ld\n", page_size);

    while (fsz < sbuf.st_size) {

        if ((sbuf.st_size - fsz) > page_size)
            copysz = page_size;
        else
            copysz = sbuf.st_size - fsz;

        // src 주소 설정
        if ((src = mmap(0, copysz, PROT_READ, MAP_SHARED, srcfd, fsz)) ==
            MAP_FAILED) {
            fprintf(stderr, "mmap error for input \n");
            printf("error : %s\n", strerror(errno));
            exit(1);
        }

        // dst 주소 설정 , 여기서 MAP_SHARED를 MAP_RPIVATE로 바꾸면? dst파일에
        // 저장되지 않는다.
        if ((dst = mmap(0, copysz, PROT_READ | PROT_WRITE, MAP_SHARED, dstfd,
                        fsz)) == MAP_FAILED) {
            fprintf(stderr, "mmap error for output\n");
            exit(1);
        }

        // src -> dst로 내용 복사
        memcpy(dst, src, copysz);

        // 메모리 해제
        munmap(src, copysz);
        munmap(dst, copysz);
        // 복사한 내용만큼 다음 메모리 위치를 이동시킬 offset 증가
        fsz += copysz;
    }

    exit(0);
}
  • result
$ /opt/homebrew/opt/gcc@15/bin/gcc-15 -std=c23 -pedantic -pthread -pedantic-errors -lm -Wall -Wextra -ggdb -Werror -o ./target/a02_mem_io_out ./src/main.c

$ ./target/a02_mem_io_out test.txt
Usage: ./target/a02_mem_io_out <source_file> <dest_file>

$ ./target/a02_mem_io_out test.txt out.txt
page_size : 16384
260308_mmap
https://younghakim7.github.io/blog/posts/260308_mmap/
Author
YoungHa
Published at
2026-03-08