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
| read | mmap | |
|---|---|---|
| Sao chép dữ liệu | Chép từ đệm nhân sang đệm của bạn | Không chép (zero-copy) |
| Truy cập ngẫu nhiên | lseek rồi read, nhiều syscall | Chỉ số mảng, không syscall |
| Nạp | Bạn quyết định đọc bao nhiêu | Lười, theo trang khi chạm |
| Chia sẻ giữa tiến trình | Khó | MAP_SHARED chia sẻ tự nhiên |
| Tệp nhỏ, đọc tuần tự một lần | Đơn giản, đủ nhanh | Thừ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ử
- Ánh xạ một tệp và in ký tự ở một vị trí bất kỳ như phần tử mảng.
- Đọc một tệp lớn bằng
readvà bằngmmap, so thời gian. - Tự viết
wc -lbằngmmap. - Truy cập ngoài kích thước tệp và quan sát
SIGBUS. - Dùng
MAP_SHAREDđể hai tiến trình chia sẻ một vùng nhớ qua một tệp. - So sánh
MAP_PRIVATEvàMAP_SHAREDkhi 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ầnreadhay 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.