[Linux] IO: memory map

memory map(mmap)์€ Linux์—์„œ ์ง€์›ํ•˜๋Š” ์ปค๋„ ์ˆ˜์ค€์˜ ํŠน์ˆ˜ํ•œ I/O ์ตœ์ ํ™” ๊ธฐ๋ฒ• ์ค‘ ํ•˜๋‚˜๋‹ค.
๋งค์šฐ ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋””์Šคํฌ๋กœ๋ถ€ํ„ฐ ์ฝ์–ด์˜ฌ๋•Œ ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค.




์ผ๋ฐ˜ ์ฝ๊ธฐ(fread)์˜ ์›๋ฆฌ

Linux์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ํŒŒ์ผ Read๋Š” fread๋ผ๋Š” ๋ช…๋ น์„ ํ†ตํ•ด์„œ ์ฒ˜๋ฆฌํ•œ๋‹ค.
๊ทธ ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค๋“ค์„ ๊ฑฐ์ณ์„œ ํŒŒ์ผ์„ ์ฝ๊ณ , ๋ฉ”๋ชจ๋ฆฌ์— ์ ์žฌํ•œ๋‹ค.

์กฐ๊ธˆ ๋ณต์žกํ•ด๋ณด์ด๊ธด ํ•˜๋Š”๋ฐ, ์•„๋ฌดํŠผ ์—ฌ๊ธฐ์„œ ์„ฑ๋Šฅ ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์€ 2๊ฐ€์ง€๋‹ค.

  1. DMA๊ฐ€ ํŒŒ์ผ์„ ์ฝ์–ด์„œ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” ์‹œ๊ฐ„

  2. ํ”„๋กœ์„ธ์„œ๊ฐ€ ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ์–ด์„œ ํ”„๋กœ์„ธ์Šค ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” ์‹œ๊ฐ„



mmap์€ ์—ฌ๊ธฐ์„œ 2๋‹จ๊ณ„์—์„œ ์†Œ๋ชจ๋˜๋Š” ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•๋ก ์ด๋‹ค.




mmap์˜ ์›๋ฆฌ

mmap ๊ธฐ๋ฐ˜์˜ read๋„ ์•ž๋ถ€๋ถ„์€ fread์™€ ํฌ๊ฒŒ ๊ณผ์ •์ด ๋‹ค๋ฅด์ง€๋Š” ์•Š๋‹ค.
OS๊ฐ€ DMA์— ์š”์ฒญํ•ด์„œ ๋””์Šคํฌ๋ฅผ ์ฝ์–ด ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์€ ๊ฐ™๋‹ค.

์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‹ค์‹œ ๋ณต์‚ฌํ•˜๋Š” ๋ถˆํ•„์š”ํ•œ ๋ถ€ํ•˜๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ด ๋‹ค๋ฅด๋‹ค.
๋Œ€์‹  ์ปค๋„ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ”„๋กœ์„ธ์Šค ๋ฉ”๋ชจ๋ฆฌ์— ์ง์ ‘ ์—ฐ๊ฒฐํ•œ๋‹ค.




mmap์˜ ์žฅ๋‹จ์ 


์žฅ์ 

  1. GB ์ˆ˜์ค€์„ ๋„˜์–ด๊ฐ€๋Š” ํฐ ํŒŒ์ผ์„ ์ฝ์„ ๋•Œ ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  2. ์บ์‹œ๊ฐ€ ๋งค์šฐ ์ž˜ ๋จนํžŒ๋‹ค. ์ด๋ฏธ load๋œ ํŒŒ์ผ์„ ๋‹ค์‹œ ์ฝ์œผ๋ฉด ๋งค์šฐ ๋น ๋ฅธ ์†๋„๋กœ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค.




๋‹จ์ 

  1. ๊ธฐ๋ณธ์ ์ธ ๋น„์šฉ์ด ํฐ ํŽธ์ด๋‹ค. ์ ๋‹นํžˆ ์ž‘์€ ํŒŒ์ผ๋“ค์„ ์ž์ฃผ ์ฝ๋Š” ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์—์„œ๋Š” ์˜คํžˆ๋ ค ๋А๋ฆด ์ˆ˜ ์žˆ๋‹ค.

  2. OS ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ์ €์žฅ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ์ผ์ด ์ž‘์„ ๊ฒฝ์šฐ ๊ณต๊ฐ„์˜ ๋‚ญ๋น„๋„ ๋ฐœ์ƒํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŽ˜์ด์ง€ ๋‹จ์œ„๊ฐ€ 4KB์ธ ์‹œ์Šคํ…œ์—์„œ 50๋ฐ”์ดํŠธ์งœ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ์™€๋ฆฌ๊ฐ€๋ฆฌํ•˜๋Š”๊ฑฐ๋ฉด 3950๋ฐ”์ดํŠธ์ฏค์„ ๊ทธ๋ƒฅ ๋‚ญ๋น„ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.






๋ฒค์น˜๋งˆํ‚นํ•ด๋ณด๊ธฐ

๋ง๋กœ๋งŒ ๋– ๋“œ๋Š”๊ฑด ์˜๋ฏธ๊ฐ€ ์—†์œผ๋‹ˆ ์ง์ ‘ ๋Œ๋ฆฌ๋ฉด์„œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด์ž.
์˜ˆ์ œ๋Š” ๋‚ ๊ฒƒ์˜ ๋ง›์„ ๋А๋ผ๊ธฐ ์œ„ํ•ด์„œ C๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
https://github.com/myyrakle/benchmarks/tree/master/fread_vs_mmap

๋จผ์ € ํฐ ํŒŒ์ผ๋ถ€ํ„ฐ ํ…Œ์ŠคํŠธํ•ด๋ณด์ž. ๋‚œ 17GB์งœ๋ฆฌ ํฐ ํ…์ŠคํŠธํŒŒ์ผ์„ ์ค€๋น„ํ–ˆ๋‹ค.

fread ๋จผ์ € ์งœ๋ดค๋‹ค.

๊ทธ๋ƒฅ ์ „์ฒด ๋‹ค ์ฝ๊ธฐ๋งŒ ํ•˜๊ณ , ์‹œ๊ฐ„์„ ์ธก์ •ํ–ˆ๋‹ค.

์•„๋ž˜๋Š” ์ „์ฒด ์ฝ”๋“œ๋‹ค.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    FILE *file = fopen("measurements.txt", "r");

    // ํŒŒ์ผ ํฌ๊ธฐ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.
    fseek(file, 0, SEEK_END);
    long filesize = ftell(file);
    rewind(file);

    // ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค.
    char *buffer = (char *)malloc(filesize);

    clock_t start = clock();

    // ํŒŒ์ผ์˜ ๋‚ด์šฉ์„ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
    size_t readsize = fread(buffer, 1, filesize, file);
    if (readsize != filesize)
    {
        printf("Failed to read file\n");
        return 1;
    }

    // NULL ์ข…๋ฃŒ ๋ฌธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    buffer[filesize] = '\0';

    fclose(file);

    clock_t end = clock();

    printf("Len %ld\n", strlen(buffer));

    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("Execution time: %f seconds\n", cpu_time_used);

    return 0;
}

๊ทธ๋ฆฌ๊ณ  ๋Œ๋ ค๋ณด๋ฉด

10์ดˆ ์ฆˆ์Œ์ด ๊ฑธ๋ ธ๋‹ค.

๊ทธ๋Ÿผ mmap์„ ์ ์šฉํ•˜๋ฉด ์–ด๋–จ๊นŒ?

ํŒŒ์ผ ๋””์Šคํฌ๋ฆฝํ„ฐ๋ฅผ ์—ด๊ณ  mmap ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ mmap ์ดˆ๊ธฐํ™”๋ฅผ ํ•ด์ฃผ๋ฉด ๋ฐ”๋กœ ์ฝ์–ด์˜จ๋‹ค.
์ด๊ฑด lazyํ•˜๊ฒŒ ์ฝ์–ด์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ธก์ •์„ ์œ„ํ•ด ์˜๋„์ ์œผ๋กœ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๊ธ€์ž๋ฅผ ์ถœ๋ ฅํ•˜๋„๋ก ํ–ˆ๋‹ค.

์•„๋ž˜๋Š” ์ „์ฒด ์ฝ”๋“œ๋‹ค.

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

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

int main()
{
    // ํŒŒ์ผ ํฌ๊ธฐ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.
    FILE *file = fopen("measurements.txt", "r");
    fseek(file, 0, SEEK_END);
    long filesize = ftell(file);
    rewind(file);

    clock_t start = clock();

    int file_descriptor = open("measurements.txt", O_RDONLY);
    if (file_descriptor == -1)
    {
        perror("Error opening file for reading");
        exit(EXIT_FAILURE);
    }

    char *buffer = mmap(0, filesize, PROT_READ, MAP_SHARED, file_descriptor, 0);
    if (buffer == MAP_FAILED)
    {
        close(file_descriptor);
        perror("Error mmapping the file");
        exit(EXIT_FAILURE);
    }

    printf("Len %ld\n", strlen(buffer));

    printf("Last Character %c\n", buffer[filesize - 1]);

    clock_t end = clock();

    double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("Execution time: %f seconds\n", cpu_time_used);

    return 0;
}

๊ทธ๋ฆฌ๊ณ  ๋Œ๋ ค๋ณด๋ฉด

๋ˆˆ์— ๋„๊ฒŒ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
์ตœ์ดˆ ํ˜ธ์ถœ์‹œ์—๋Š” 5์ดˆ๊ฐ€ ์•ˆ๊ฑธ๋ ธ๊ณ , ์ดํ›„์—๋Š” ์บ์‹œ๋ฅผ ํƒ€์„œ 2์ดˆ๋„ ๊ฑธ๋ฆฌ์ง€ ์•Š์•˜๋‹ค.


๊ทธ๋Ÿผ ์ž‘์€ ํŒŒ์ผ์„ ๋‹ค๋ฃฐ ๋•Œ๋Š” ์–ด๋–จ๊นŒ?
์ข€ ์ ๋‹นํžˆ ์ž‘์€ ํ…์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์ค€๋น„ํ•ด์„œ ๋ฐ˜๋ณต์„ ๋Œ๋ ค๋ดค๋‹ค.

๋จผ์ € fread๋Š” 5000๋ฒˆ ์ •๋„๋งŒ ๋ฐ˜๋ณต์„ ๋Œ๋ ค๋ดค๋‹ค.

fread๋Š” 0.15์ดˆ ์ •๋„๊ฐ€ ๊ฑธ๋ ธ๋‹ค. ๋ฌด๋‚œํ•˜๋‹ค.


mmap๋„ ๋น„์Šทํ•˜๊ฒŒ 0.15์ดˆ ์ •๋„๊ฐ€ ๊ฑธ๋ ธ๋‹ค.

ํ‰๋ฒ”ํ•œ ๋ ˆ๋ฒจ์—์„œ๋Š” ์„ฑ๋Šฅ์ฐจ๊ฐ€ ๊ฑฐ์˜ ๋‚˜์ง€ ์•Š๋Š”๋‹ค.

์ด๋ฒˆ์—๋Š” ๋‹จ์œ„๋ฅผ ์ข€๋” ์˜ฌ๋ ค๋ดค๋‹ค.

์ด๋Ÿฌ๋ฉด ์ด์ œ ์กฐ๊ธˆ์”ฉ fread๊ฐ€ ๋” ๋นจ๋ผ์ง€๊ธฐ ์‹œ์ž‘ํ•œ๋‹ค. ํŒŒ์ผ์ด ์ž‘๊ณ , ์ฝ๊ธฐ๊ฐ€ ๋นˆ๋ฒˆํ• ์ˆ˜๋ก ๊ฒฉ์ฐจ๋Š” ๋” ๋ฒŒ์–ด์ง„๋‹ค.

๊ทธ๋ž˜์„œ ์ผ๋ฐ˜์ ์ธ usecase์—์„œ๋Š” ๊ตณ์ด mmap์„ ์šฐ์„ ์ ์œผ๋กœ ๊ณ ๋ คํ•  ํ•„์š”๋Š” ์—†๋‹ค.



์ฐธ์กฐ
https://coder-in-war.tistory.com/entry/Linux-Kernel-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%A7%B5-%ED%8C%8C%EC%9D%BCmmap
https://www.researchgate.net/post/What_is_the_actual_difference_between_fread_and_mmap_functions_in_linux_unix
https://wallmusings.in/2020/03/20/fread-vs-mmap/
https://wallmusings.in/2020/03/05/when-not-to-use-mmaps/