0
点赞
收藏
分享

微信扫一扫

文件系统笔记

  这段时间一直都在处理整个系统的bring up, 对系统盘 数据盘挂载 分区, uboot 启动内核、挂载根文件系统等问题debug。目前记录一下学习到的文件系统知识。

文件系统设计了如下特性

  • 采取树形结构、文件夹设计
  • 对热点文件进行缓存,便于读写
  • 采用索引结构,便于查找分类
  • 维护一套数据结构用于记录哪些文档正在被哪些任务使用

所以来看看Linux文件系统的设计

  硬盘中我们以块为存储单元,而在文件系统中,我们需要有一个存储块信息的基本结构体,这就是文件系统的基石​​inode​​​,其源码如下。​​inode​​​意为​​index node​​,即索引节点

/*
* Structure of an inode on the disk
*/
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 最近一次更改 inode 的时间*/
__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 */
union {
struct {
__le32 l_i_version;
} linux1;
struct {
__u32 h_i_translator;
} hurd1;
struct {
__u32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__le32 i_block[EXT4_N_BLOCKS];/* 该成员变量实际存储了文件内容的每一个块Pointers to blocks */
__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 */
union {
struct {
__le16 l_i_blocks_high; /* were l_i_reserved1 */
__le16 l_i_file_acl_high;
__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
__le16 l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
__le16 l_i_reserved;
} linux2;
struct {
__le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */
__u16 h_i_mode_high;
__u16 h_i_uid_high;
__u16 h_i_gid_high;
__u32 h_i_author;
} hurd2;
struct {
__le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */
__le16 m_i_file_acl_high;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
__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_inode 结构中的i_block 实际存储了文件内容的每一个块,在ext2和ext3格式的文件系统中,我们用前12个块存放对应的文件数据,每个块4KB,如果文件较大放不下,则需要使用后面几个间接存储块来保存数据,下图很形象的表示了其存储原理

文件系统笔记_文件系统

  该存储结构带来的问题是对于大型文件,我们需要多次调用才可以访问对应块的内容,因此访问速度较慢。为此,ext4提出了新的解决方案:​​Extents​​。简单的说,Extents以一个树形结构来连续存储文件块,从而提高访问速度,大致结构如下图所示。

文件系统笔记_数据_02

 

 

主要结构体为节点​​ext4_extent_header​​​,​​eh_entries​​ 表示这个节点里面有多少项。这里的项分两种:

  • 如果是叶子节点,这一项会直接指向硬盘上的连续块的地址,我们称为数据节点​​ext4_extent​​;
  • 如果是分支节点,这一项会指向下一层的分支节点或者叶子节点,我们称为索引节点​​ext4_extent_idx​​​。这两种类型的项的大小都是 12 个​​byte​

  如果文件不大,​​inode​​​ 里面的 ​​i_block​​​ 中,可以放得下一个 ​​ext4_extent_header​​​ 和 4 项 ​​ext4_extent​​​。所以这个时候,​​eh_depth​​​ 为 0,也即 ​​inode​​​ 里面的就是叶子节点,树高度为 0。如果文件比较大,4 个 ​​extent​​​ 放不下,就要分裂成为一棵树,​​eh_depth>0​​​ 的节点就是索引节点,其中根节点深度最大,在 ​​inode​​​ 中。最底层 ​​eh_depth=0​​​ 的是叶子节点。除了根节点,其他的节点都保存在一个块 4k 里面,4k 扣除 ​​ext4_extent_header​​​ 的 12 个 ​​byte​​​,剩下的能够放 340 项,每个 ​​extent​​​ 最大能表示 128MB 的数据,340 个 ​​extent​​ 会使你表示的文件达到 42.5GB。这已经非常大了,如果再大,我们可以增加树的深度。

  所以,我们可以通过​​inode​​​来表示一系列的块,从而构成了一个文件。在硬盘上,通过一系列的​​inode​​​,我们可以存储大量的文件。但是我们尚需要一种方式去存储和管理​​inode​​,这就是位图。同样的,我们会用块位图去管理块的信息

​inode​​和块是文件系统的最小组成单元,在此之上还有多级系统,大致有如下这些:

  • 块组:存储一块数据的组成单元,数据结构为​​ext4_group_desc​​​。这里面对于一个块组里的​​inode​​​ 位图​​bg_inode_bitmap_lo​​​、块位图​​bg_block_bitmap_lo​​​、​​inode​​​ 列表​​bg_inode_table_lo​​均有相应的定义。一个个块组,就基本构成了我们整个文件系统的结构。block的大小可以有区别。典型的block大小是1024 bytes或者4096 bytes。这个大小在创建ext2、ext3文件系统的时候被决定,mkfs –t ext2/3 –b xx就可以设定块大小了!一个硬盘分区上的block计数是从0开始的,总的来说,block这个概念好理解。
  • 块组描述符表:多个块组的描述符构成的表
  • 硬盘分区上所有的block被聚在一起分成几个大的block group。其中每个block group中有多少个block是固定的。从下面的图可以看出来!每个block group都相对应一个group descriptor,每个group descriptor当中有几个重要的block指针,指向block group中的inode tableblock bitmap和inode bitmap
  • 超级块:对整个文件系统的情况进行描述,即​​ext4_super_block​​​,存储全局信息,如整个文件系统一共有多少​​inode​​​:​​s_inodes_count​​​;一共有多少块:​​s_blocks_count_lo​​​,每个块组有多少​​inode​​​:​​s_inodes_per_group​​​,每个块组有多少块:​​s_blocks_per_group​​ 等。
  • 引导块:对于整个文件系统,我们需要预留一块区域作为引导区用于操作系统的启动,所以第一个块组的前面要留 1K,用于启动引导区。

文件系统笔记_文件系统_03

 

 文件系统笔记_数据_04

 

   Super block即为超级块,它是硬盘分区开头——从 byte 1024开始往后的一部分数据。由于 block size最小是 1024 bytes,所以super block可能是在block 1中(此时block 的大小正好是 1024 bytes);也就是超级块位于第1个逻辑块内,由于第一个块组预留了1KB的内容作为系统引导,因此在该块组超级块的位置在1KB偏移处,而其它备份块组中的超级块都在该块组偏移为0的地方。超级块会占用1个逻辑块的空间(实际占用空间要小于该值),也就是说块组描述符(ext2_group_desc)是在4KB偏移的地方
超级块中的数据其实就是文件卷的控制信息部分,也可以说它是卷资源表,有关文件卷的大部分信息都保存在这里。例如:硬盘分区中每个block的大小、硬盘分区上一共有多少个block group、以及每个block group中有多少个inode。

文件系统笔记_文件系统_05

 

 

超级块和块组描述符表都是全局信息,而且这些数据很重要。如果这些数据丢失了,整个文件系统都打不开了,这比一个文件的一个块损坏更严重。所以,这两部分我们都需要备份,但是采取不同的策略。

  • 默认策略:在每个块中均保存一份超级块和块组描述表的备份
  • sparse_super策略:采取稀疏存储的方式,仅在块组索引为 0、3、5、7 的整数幂里存储。
  • Meta Block Groups策略:我们将块组分为多个元块组(Meta Block Groups),每个元块组里面的块组描述符表仅仅包括自己的内容,一个元块组包含 64 个块组,这样一个元块组中的块组描述符表最多 64 项。这种做法类似于​​merkle tree​​,可以在很大程度上优化空间。

文件系统笔记_文件系统_06

 

   为了便于文件的查找,我们必须要有索引,即文件目录。其实目录本身也是个文件,也有 ​​inode​​​。​​inode​​​ 里面也是指向一些块。和普通文件不同的是,普通文件的块里面保存的是文件数据,而目录文件的块里面保存的是目录里面一项一项的文件信息。这些信息我们称为 ​​ext4_dir_entry​​​。这里有两个版本,第二个版本 ​​ext4_dir_entry_2​​​ 是将一个 16 位的 ​​name_len​​​,变成了一个 8 位的 ​​name_len​​​ 和 8 位的 ​​file_type​​。

  在目录文件的块中,最简单的保存格式是列表,就是一项一项地将 ​​ext4_dir_entry_2​​​ 列在哪里。每一项都会保存这个目录的下一级的文件的文件名和对应的 ​​inode​​​,通过这个 ​​inode​​​,就能找到真正的文件。第一项是“.”,表示当前目录,第二项是“…”,表示上一级目录,接下来就是一项一项的文件名和 ​​inode​​。有时候,如果一个目录下面的文件太多的时候,我们想在这个目录下找一个文件,按照列表一个个去找太慢了,于是我们就添加了索引的模式

  如果我们要查找一个目录下面的文件名,可以通过名称取哈希。如果哈希能够匹配上,就说明这个文件的信息在相应的块里面。然后打开这个块,如果里面不再是索引,而是索引树的叶子节点的话,那里面还是 ​​ext4_dir_entry_2​​ 的列表,我们只要一项一项找文件名就行。通过索引树,我们可以将一个目录下面的 N 多的文件分散到很多的块里面,可以很快地进行查找。

文件系统笔记_文件系统_07

 

 

软链接和硬链接的存储格式

  硬链接与原始文件共用一个 ​​inode​​​ ,但是 ​​inode​​​ 是不跨文件系统的,每个文件系统都有自己的 ​​inode​​​ 列表,因而硬链接是没有办法跨文件系统的。而软链接不同,软链接相当于重新创建了一个文件。这个文件也有独立的 ​​inode​​,只不过打开这个文件看里面内容的时候,内容指向另外的一个文件。这就很灵活了。我们可以跨文件系统,甚至目标文件被删除了链接文件也依然存在,只不过指向的文件找不到了而已。

文件系统笔记_文件系统_08

 转载​​来自​​

 

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子

举报

相关推荐

0 条评论