前言
网上关于BIO和块设备读写流程的文章何止千万,但是能够让你彻底读懂读明白的文章实在难找,可以说是越读越糊涂!
我曾经跨过山和大海 也穿过人山人海
我曾经问遍整个世界 从来没得到答案
本文用一个最简单的read(fd, buf, 4096)的代码,分析它从开始读到读结束,在整个Linux系统里面波澜壮阔的一生。本文涉及到的代码如下:
#include <unistd.h>
#include <fcntl.h>
main()
{
int fd;
char buf[4096];
sleep(30); //run ./funtion.sh to trace vfs_read of this process
fd = open("file", O_RDONLY);
read(fd, buf, 4096);
read(fd, buf, 4096);
}
本文的写作宗旨是:绝不装逼,一定要简单,简单,再简单!
本文适合:已经读了很多乱七八糟的block资料,但是没打通脉络的读者;
本文不适合:完全不知道block子系统是什么的读者,和完全知道block子系统是什么的读者
Page cache与预读
在Linux中,内存充当硬盘的page cache,所以,每次读的时候,会先check你读的那一部分硬盘文件数据是否在内存命中,如果没有命中,才会去硬盘;如果已经命中了,就直接从内存里面读出来。如果是写的话,应用如果是以非SYNC方式写的话,写的数据也只是进内存,然后由内核帮忙在适当的时机writeback进硬盘。
代码中有2行read(fd, buf, 4096),第1行read(fd, buf, 4096)发生的时候,显然”file”文件中的数据都不在内存,这个时候,要执行真正的硬盘读,app只想读4096个字节(一页),但是内核不会只是读一页,而是要多读,提前读,把用户现在不读的也先读,因为内核怀疑你读了一页,接着要连续读,怀疑你想读后面的。与其等你发指令,不如提前先斩后奏(存储介质执行大块读比多个小块读要快),这个时候,它会执行预读,直接比如读4页,这样当你后面接着读第2-4页的硬盘数据的时候,其实是直接命中了。
所以这个代码路径现在是 :