这个特性的存在要归结于文件系统在磁盘上的布局,文件需要元数据以便更高效的被系统 所管理,元数据分为两类,一类为抽象给用户的元数据,比如所有者,访问控制列表,修改日期等等,另一类是抽象给系统的,比如块数,块的位置等等,鉴于文件 的大小都有一个平均值并且为了节省元数据占有的固定空间,很多文件系统都采用了间接磁盘寻址,以ext2为例,简单的说如果一个文件的大小在12块之内, 那么通过12个直接寻址指针就可以通透整个文件,如果超过一定大小的话,那么就需要一个间接的指针来指向一个专门的寻址块,然后该寻址块再指向数据块,这 样就会有多达“块大小/指针大小+12”个数据指针,如果文件更大,那么还有一个三级指针或者别的,如此在ext2_inode这个磁盘inode中就会 只需要16个指针就可以了,其中12个直接寻址指针,1个间接指针,1个二级指针...这大大节省了磁盘inode的元数据占用的磁盘空间,之所以用12 个指针来直接寻址是因为在统计意义上大部分ext2的文件都在12个块以内。以上的解决方案看来不错,节省了空间,但是任何事情都不是完美的,节省了空间 很多时候意味着损失了时间,空间换时间,要么就是时间换空间,鱼与熊掌不可得兼啊。
果然没有猜错,确实这种间接寻址模式浪费了读写磁盘的时间,如果需要读写磁盘,首先需要做的就是将磁盘的块映射到内存,其实也就是内存的缓冲区,描述起来 就是一个buffer_head(简称bh)表示的一个内存区域。一个bh表示一个磁盘块,这个对应关系如果应用到直接寻址块,那么很简单,块和内存中的 bh直接对应就可以了,不需要额外的io,也就是说所有的io都是针对数据的,“得到块”这件事不需要io,但是一旦涉及到间接寻址块,那么就需要一次或 者多次额外的io,这些次io不是针对文件的数据的,而是为了得到间接块号和间接块的位置,这些io是不可避免的,因此额外的开销也不可避免,毕竟我们节 省了空间,可是可以通过设计将额外的开销降低到最低,具体怎么做到呢?就是不为间接寻址的额外的io单独做一次io,而是将其和前面的数据io连在一起一 起提交,因为直接指针和间接指针很大可能性是连续的磁盘块,这样会使得磁盘io紧凑地在连续块进行,正如mpage.c中 do_mpage_readpage函数的注释所说的那样。
int mpage_readpages(struct address_space *mapping, struct list_head *pages, unsigned nr_pages, get_block_t get_block)
{