Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 42.326 phút đọc

mmap, ánh xạ tệp vào bộ nhớ

Sau bài này bạn sẽ làm được

  • Ánh xạ một tệp bằng mmap và truy cập như mảng
  • Giải thích zero-copy và nạp lười theo trang
  • So sánh mmap với read cho tệp lớn
  • Nhớ munmap và kiểm tra MAP_FAILED

mmap ánh xạ một tệp thẳng vào không gian địa chỉ, để bạn truy cập nó như một mảng trong bộ nhớ, không cần read hay bộ đệm. Với tệp lớn và truy cập ngẫu nhiên, nó nhanh hơn hẳn.

#mmap là gì

mmap
Ánh xạ bộ nhớ. Nhân làm cho một vùng địa chỉ trong tiến trình trỏ tới nội dung một tệp. Đọc từ vùng đó là đọc tệp; ghi vào đó là ghi tệp. Không có lời gọi read hay write tường minh; nhân nạp từng trang khi bạn chạm tới.
Chữ ký
#include <sys/mman.h>

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/* addr    : NULL de nhan tu chon dia chi
   length  : so byte anh xa
   prot    : PROT_READ, PROT_WRITE, PROT_EXEC
   flags   : MAP_PRIVATE (ghi rieng) hoac MAP_SHARED (ghi xuong tep)
   fd      : mo ta tep da open
   offset  : bat dau tu byte nao trong tep (boi cua kich thuoc trang)
   tra ve  : con tro toi vung anh xa, hoac MAP_FAILED khi loi */

int munmap(void *addr, size_t length);      /* go anh xa */

#Đọc tệp như mảng

mmap_demo.c
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }

    struct stat st;
    fstat(fd, &st);                 /* lay kich thuoc tep */

    char *data = mmap(NULL, (size_t)st.st_size,
                      PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED) { perror("mmap"); return 1; }

    /* Gio truy cap tep NHU MOT MANG trong bo nho */
    printf("byte thu 1000000 = %c\n", data[1000000]);
    printf("ky tu dau tien   = %c\n", data[0]);

    munmap(data, (size_t)st.st_size);   /* BAT BUOC go anh xa */
    close(fd);
    return 0;
}
terminal
gcc mmap_demo.c -o mmap_demo && ./mmap_demo bigfile.dat
byte thu 1000000 = x
ky tu dau tien   = H

#Ưu điểm

readmmap
Sao chép dữ liệuChép từ đệm nhân sang đệm của bạnKhông chép (zero-copy)
Truy cập ngẫu nhiênlseek rồi read, nhiều syscallChỉ số mảng, không syscall
NạpBạn quyết định đọc bao nhiêuLười, theo trang khi chạm
Chia sẻ giữa tiến trìnhKhóMAP_SHARED chia sẻ tự nhiên
Tệp nhỏ, đọc tuần tự một lầnĐơn giản, đủ nhanhThừa

#Ví dụ wc bằng mmap

Đếm dòng bằng mmap
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
    int fd = open(argv[1], O_RDONLY);
    struct stat st;
    fstat(fd, &st);

    if (st.st_size == 0) { printf("0\n"); return 0; }   /* tep rong */

    char *data = mmap(NULL, (size_t)st.st_size,
                      PROT_READ, MAP_PRIVATE, fd, 0);
    if (data == MAP_FAILED) { perror("mmap"); return 1; }

    long dong = 0;
    for (off_t i = 0; i < st.st_size; ++i)
        if (data[i] == '\n') ++dong;       /* duyet nhu mang */

    printf("%ld\n", dong);

    munmap(data, (size_t)st.st_size);
    close(fd);
    return 0;
}

#Cần thận trọng

Không kiểm tra
struct stat st; fstat(fd, &st); char *data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == NULL) return 1; /* SAI: mmap tra MAP_FAILED, khong NULL */ process(data, st.st_size); /* tep rong -> mmap da loi -> sap */
Kiểm tra đầy đủ
struct stat st; if (fstat(fd, &st) != 0) { perror("fstat"); return 1; } if (st.st_size == 0) { /* tep rong: xu ly rieng */ return 0; } char *data = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { perror("mmap"); return 1; } process(data, (size_t)st.st_size); munmap(data, (size_t)st.st_size);

Tự làm thử

  1. Ánh xạ một tệp và in ký tự ở một vị trí bất kỳ như phần tử mảng.
  2. Đọc một tệp lớn bằng read và bằng mmap, so thời gian.
  3. Tự viết wc -l bằng mmap.
  4. Truy cập ngoài kích thước tệp và quan sát SIGBUS.
  5. Dùng MAP_SHARED để hai tiến trình chia sẻ một vùng nhớ qua một tệp.
  6. So sánh MAP_PRIVATE và MAP_SHARED khi ghi: cái nào xuống tệp, cái nào không.

Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.

Tóm tắt

  • mmap ánh xạ một tệp vào không gian địa chỉ để truy cập như mảng, không cần read hay bộ đệm.
  • Nạp lười theo trang: chỉ trang bạn chạm mới được đọc, nên ánh xạ tệp khổng lồ vẫn rẻ.
  • Ưu điểm: zero-copy, truy cập ngẫu nhiên nhanh, nạp lười, chia sẻ giữa tiến trình.
  • Tốt cho tệp lớn truy cập ngẫu nhiên; với tệp nhỏ đọc tuần tự, read đơn giản là đủ.
  • Kiểm tra MAP_FAILED (không phải NULL), xử lý tệp rỗng, ở trong phạm vi kích thước, và nhớ munmap.