https://zhuanlan.zhihu.com/p/78724124
根据文件名索引到文件内容
表面上,用户通过文件名,打开文件。实际上,系统内部这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的block,读出数据。
现在举一个具体的例子,来说明文件是怎么读取到的,比如读取/home/bzw/test里的内容,目录结构如下图
假设文件系统的的简单分区如下
获取home对应的inode号:先找根目录’/'的inode(不考虑缓存):根目录的inode号可以从super_block中获取,ext4文件系统的根目录inode号为2(xfs文件系统根目录inode号是64,ceph文件系统根目录inode号是1),所以在索引区读取inode号为2存的inode内容。假如inode中存的block地址是1000,那么去数据区读取地址为1000的block存的内容,内容如下图所示。
地址为1000的block存的内容
地址为1000的block里面存了20个目录项(struct ext4_dir_entry),可以找到目录home对应的inode号为100。
获取bzw对应的inode号:上一步获取到了目录home的inode号,在索引区读取inode号为100存的inode内容。假如inode中存的block地址为2000,那么去读地址为2000的block存的内容,内容如下图所示。
地址为2000的block存的内容
地址为2000的block里面存了3个目录项(struct ext4_dir_entry),可以找到目录bzw对应的inode号为200。
获取test对应的inode号:上一步获取到了目录bzw的inode号,在索引区读取inode号为200存的inode内容。假如inode中存的block地址为3000,那么去读地址为3000的block存的内容,内容如下图所示。
地址为3000的block存的内容
地址为3000的block里面存了2个目录项(struct ext4_dir_entry),可以找到文件test对应的inode号为300。
获取test对应的内容:上一步获取到了文件test的inode号,在索引区读取inode号为300存的inode内容。假如inode中存的block地址为4000,那么去读地址为4000的block存的内容。这个时候就完成了操作。
这里注意如果test内容很大,那么在inode里面存的block地址就不止一个了。
可以以ext4中的struct ext4_inode为例
struct ext4_inode {
__le16 i_mode; /* File mode /
__le16 i_uid; / Low 16 bits of Owner Uid /
__le32 i_size_lo; / Size in bytes /
__le32 i_atime; / Access time /
__le32 i_ctime; / Inode Change time /
__le32 i_mtime; / Modification time /
__le32 i_dtime; / Deletion Time /
__le16 i_gid; / Low 16 bits of Group Id /
__le16 i_links_count; / Links count /
__le32 i_blocks_lo; / Blocks count /
__le32 i_flags; / File flags /
…
__le32 i_block[EXT4_N_BLOCKS];/ Pointers to blocks / 这里面存的就是block的地址
__le32 i_generation; / File version (for NFS) /
__le32 i_file_acl_lo; / File ACL /
__le32 i_size_high;
__le32 i_obso_faddr; / Obsoleted fragment address /
…
__le16 i_extra_isize;
__le16 i_checksum_hi; / crc32c(uuid+inum+inode) BE /
__le32 i_ctime_extra; / extra Change time (nsec << 2 | epoch) /
__le32 i_mtime_extra; / extra Modification time(nsec << 2 | epoch) /
__le32 i_atime_extra; / extra Access time (nsec << 2 | epoch) /
__le32 i_crtime; / File Creation time /
__le32 i_crtime_extra; / extra FileCreationtime (nsec << 2 | epoch) /
__le32 i_version_hi; / high 32 bits for 64-bit version /
__le32 i_projid; / Project ID */
};
EXT4_N_BLOCKS定义如下,即为15,其中EXT4_IND_BLOCK为12,表示有12个直接索引表,然后有1个一级间接索引表,1个二级间接索引表,1个三级间接索引表。
/*
- Constants relative to the data blocks
*/
#define EXT4_NDIR_BLOCKS 12
#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS
#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1)
#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1)
#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1)
inode_hashtable
那么根目录inode号为2是怎么获取到的?
我们可以知道,在mount文件系统时,会在内存中建立一个super_block;而在struct super_block中有struct dentry *s_root成员,s_root就指向的是根目录的dentry,而在struct dentry中有struct inode *d_inode成员,保存的是根目录的inode,inode号就可以从inode中获取。不需要再去读索引区中inode为2的inode内容,因为这部分内容已经在缓存中了,只需要去根目录具体所指的block区读取目录项即可。