每个非空的普通文件都是由一组数据块组成。这些块或者由文件内的相对位置(文件块号)来表示,或者由磁盘分区内的位置(它们的逻辑块号)来表示。
从文件内的偏移量f导出相应数据块的逻辑块号需要两个步骤:
· 从偏移量f导出文件的块号,即偏移量f处的字符所在的块索引。
· 把文件的块号转化为相应的逻辑块号。
因为Linux文件不包含任何控制字符,因此,导出文件的第f个字符所在的文件块号是相当容易的:只是用f除以文件系统块的大小,并取整即可。
例如,让我们假定块的大小为4KB。如果f小于4096,那么这个字符就在文件的第一个数据块中,其文件的块号为0。如果f等于或大于4096而小于8192,则这个字符就在文件块号为1的数据块中等等。
只用关心文件的块号确实不错。但是,由于Ext2文件的数据块在磁盘上并不是相邻的,因此把文件的块号转化为相应的逻辑块号可不是那么直接了当。
因此,Ext2文件系统必须提供一种方法,用这种方法可以在磁盘上建立每个文件块号与相应逻辑块号之间的关系。在索引节点内部部分实现了这种映射,这种映射也包括一些专门的数据块,可以把这些数据块看成是用来处理大型文件的索引节点的扩展。
磁盘索引节点的i_block域是一个有EXT2_N_BLOCKS个元素且包含逻辑块号的数组。在下面的讨论中,我们假定EXT2_N_BLOCKS的默认值为15,如图9.4所示,这个数组表示一个大型数据结构的初始化部分。正如你从图中所看到的,数组的15个元素有4种不同的类型:
· 最初的12个元素产生的逻辑块号与文件最初的12个块对应,即对应的文件块号从0到11。
· 索引12中的元素包含一个块的逻辑块号,这个块代表逻辑块号的一个二级数组。这个数组对应的文件块号从12到b/4+11,这里b是文件系统的块大小(每个逻辑块号占4个字节,因此我们在式子中用4做除数)。因此,内核必须先用指向一个块的指针访问这个元素,然后,用另一个指向包含文件最终内容的块的指针访问那个块。
· 索引13中的元素包含一个块的逻辑块号,而这个块包含逻辑块号的一个二级数组;这个二级数组的数组项依次指向三级数组,这个三级数组存放的才是逻辑块号对应的文件块号,范围从b/4+12 到(b/4)2+(b/4)+11。
· 最后,索引14中的元素利用了三级间接索引:第四级数组中存放的才是逻辑块号对应的文件块号,范围从(b/4)2+(b/4)+12 到(b/4)3+(b/4)2+(b/4)+11。
注意这种机制是如何支持小文件的。如果文件需要的数据块小于12,那么两次访问磁盘就可以检索到任何数据:一次是读磁盘索引节点i_block数组的一个元素,另一次是读所需要的数据块。对于大文件来说,可能需要3-4次的磁盘访问才能找到需要的块。实际上,这是一种最坏的估计,因为目录项、缓冲区及页高速缓存都有助于极大地减少实际访问磁盘的次数。
也要注意文件系统的块大小是如何影响寻址机制的,因为大的块大小允许Ext2把更多的逻辑块号存放在一个单独的块中。表9.2显示了对每种块大小和每种寻址方式所存放文件大小的上限。例如,如果块的大小是1024字节,并且文件包含的数据最多为268KB,那么,通过直接映射可以访问文件最初的12KB数据,通过简单的间接映射可以访问剩余的13KB到268KB的数据。对于4096字节的块,两次间接就完全满足了对2GB文件的寻址(2GB是32位体系结构上的Ext2文件系统所允许的最大值)。
表9.2可寻址的文件数据块大小的界限
块大小 |
直接 |
一次间接 |
二次间接 |
三次间接 |
1024 |
12 KB |
268 KB |
63.55 MB |
2 GB |
2048 |
24 KB |
1.02 MB |
513.02 MB |
2 GB |
4096 |
48 KB |
4.04 MB |
2 GB |
- |