Bài 41.326 phút đọc
Tầng thấp so với stdio
Sau bài này bạn sẽ làm được
- So sánh vào ra tầng thấp với stdio trên năm tiêu chí
- Đo ảnh hưởng kích thước bộ đệm tới hiệu năng
- Chuyển đổi giữa fd và FILE bằng fileno và fdopen
- Chọn đúng tầng cho từng loại vào ra
open/read/write nói thẳng với nhân. fopen/fread/fwrite là một lớp thư viện có bộ đệm nằm trên. Bài này đo chênh lệch giữa chúng, và con số giải thích vì sao bộ đệm tồn tại.
#Hai tầng vào ra
| open/read/write | fopen/fread/fwrite | |
|---|---|---|
| Tầng | Lời gọi hệ thống trực tiếp | Thư viện chuẩn C |
| Bộ đệm | Không | Có |
| Khả chuyển | POSIX | Chuẩn C, mọi nền tảng |
| Kiểu | int fd | FILE * |
| Kiểm soát | Chi tiết | Ít hơn |
| Dùng cho | socket, ống, thiết bị, vào ra lớn | tệp thường, vào ra nhỏ |
Bộ đệm của stdio
Thư viện chuẩn giữ một vùng đệm trong bộ nhớ tiến trình.
fwrite chép vào đệm và trả về ngay; chỉ khi đệm đầy (hoặc bạn fflush) nó mới gọi một write thật. Nhờ vậy nghìn lần fputc chỉ thành vài lời gọi hệ thống.#Đo hiệu năng bộ đệm
perf.c, đọc 20 MB với các kích thước bộ đệm
int main(int argc, char **argv) {
int bufsz = (argc > 1) ? atoi(argv[1]) : 4096;
int fd = open("big.dat", O_RDONLY);
char *buf = malloc(bufsz);
long tong = 0, lan = 0;
ssize_t n;
clock_t t0 = clock();
while ((n = read(fd, buf, bufsz)) > 0) {
tong += n;
++lan;
}
clock_t t1 = clock();
printf("buffer %6d byte: %ld lan read, %ld byte, %.0f ms\n",
bufsz, lan, tong, 1000.0 * (t1 - t0) / CLOCKS_PER_SEC);
close(fd);
free(buf);
return 0;
}terminal
for sz in 1 512 4096 65536; do ./perf.exe $sz; done
buffer 1 byte: 20971520 lan read, 20971520 byte, 53547 ms buffer 512 byte: 40960 lan read, 20971520 byte, 133 ms buffer 4096 byte: 5120 lan read, 20971520 byte, 34 ms buffer 65536 byte: 320 lan read, 20971520 byte, 22 ms
#Chuyển đổi fd và FILE
fdopen.c, đi lại giữa hai tầng
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int fd = open("fd.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
FILE *f = fdopen(fd, "w"); /* fd -> FILE*: boc de dung fprintf */
fprintf(f, "ghi qua FILE* boc quanh fd\n");
fclose(f); /* dong ca FILE va fd ben trong */
FILE *r = fopen("fd.txt", "r");
int rfd = fileno(r); /* FILE* -> fd: lay so fd ben trong */
printf("fileno cua FILE* = %d\n", rfd);
char buf[64];
fgets(buf, sizeof buf, r);
printf("noi dung: %s", buf);
fclose(r);
return 0;
}terminal
gcc -O2 -Wall -Wextra fdopen.c -o fdopen.exe && ./fdopen.exe
fileno cua FILE* = 3 noi dung: ghi qua FILE* boc quanh fd
#Chọn tầng nào
Tự làm thử
- Đo thời gian đọc một tệp lớn với bộ đệm 1 byte, 512 byte, 4 KB, 64 KB.
- Trên Linux, chạy
strace -cđể đếm sốreadở mỗi kích thước bộ đệm. - So sánh đọc bằng
readvới đọc bằngfreadcùng logic. - Bọc một fd bằng
fdopenvà dùngfprintfghi vào nó. - Lấy
filenocủa mộtFILE*rồi gọifstatđể đọc kích thước tệp. - Trộn
fwritevàwritetrên cùng tệp và quan sát thứ tự byte sai khi quênfflush.
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
- Tầng thấp nói thẳng với nhân, không đệm; stdio là lớp có bộ đệm trên nó.
- Đọc từng byte chậm hơn nghìn lần vì mỗi syscall tốn; bộ đệm 4 KB trở lên là đủ.
fdopenbọc fd thànhFILE*,filenolấy fd từFILE*; đừng trộn hai tầng trên một tệp.- Dùng stdio cho tệp thường, tầng thấp cho socket và ống và vào ra lớn.
- Nhớ
fflushtrướcforkvàexec, hoặc dùng tầng thấp khi làm việc với tiến trình.