洞察纵观鸿蒙next版本,如何凭借FinClip加强小程序的跨平台管理,确保企业在数字化转型中的高效运营和数据安全?
1067
2022-09-23
ExtX系列文件系统的结构学习_sigaction-ChinaUnix博客
一、概述: 现如今在Linux上主要流行的ExtX系列文件系统主要是Ext2、Ext3、Ext4,如它们名字中的数字排列一样: The Second Extended File System(Ext2)文件系统是早期Linux系统中的标准文件系统,是通过对Minix的文件系统进行扩展而得到的,Ext3是一种日志文件系统,是对Ext2系统的扩展,它兼容Ext2,而Ext4又是Ext3的扩展,但它存储数据的结构发生了很大变化,直接导致了它支持 1EB(1,048,576TB, 1EB=1024PB, 1PB=1024TB)的文件系统,以及 16TB 的文件。08年底发布的 2.6.28 Linux kernel开始正式支持新的文件系统 Ext4。 二、组织结构: 1、总体布局: 在Ext2、Ext3中,存储数据的结构是按“块”来组织的,而不是“扇区”。块的大小随文件系统的大小而有所不同(一般是512字节、1024字节等)。而一定数量的块又组成一个块组,每个块组的起始部分有多种多样的描述该块组各种属性的元数据结构(磁盘布局结构): 每一个分区的前两个扇区存放系统启动用的信息等,所以真正的文件系统是从第三个扇区开始算的。其实主要的结构包括超级块(并不是每一个组都有一个超级块,因为太浪费,系统一般用的也就是第一个而已,不过会在其他组做备份(稀疏超级块机制)),块组描述符(如其名字描述整个块组信息的,包括节点位图、节点表的位置等),块位图(记录空闲块和使用块等信息),节点位图(记录节点使用和空闲情况),节点表(一个节点标识一个文件,记录文件所有信息)和数据区(目录的数据区的内容是各个目录项,文件的数据区就是文件内容,所以目录、文件的存储方式都一样,在linux上一切皆文件) 2、文件存放原理: #160;在ExtX系列文件系统中,文件由inode(节点,包含有文件的所有信息)进行唯一标识。一个文件可能对应多个文件名,只有在所有文件名都被删除后,该文件才会被删除。Ext2和Ext3文件系统采用三级间接块来存储数据块指针,并以块为单位分配空间。(等在下面看到inode的数据结构体里面的各项成员就更加清楚了)。 #160;如下图所示Direct Blocks里存的就是一个块号,那个块里就是数据区了,Indirect blocks就是间接指针的意思 ,double indirect就是二级间接,triple#160; indirect就是三级间接了···(这样设计也是想多存点数据,但跟Ext4比起来还是不够合理) 3、主要数据结构(一般定义在/usr/include/linux/ext2_fs.h文件中(Ext2和Ext3结构基本一样)): (1)超级块: 超级块的起始位置始终为其所在分区的第1024个字节(偏移两个扇区)。 struct ext2_super_block { __le32 s_inodes_count;// 文件系统中inode的总数 __le32 s_blocks_count;// 文件系统中块的总数 ············· __le32 s_log_block_size;// 用以计算块的大小(1024算术左移该值即为块大小) __le32 s_blocks_per_group;// 每个块组中块的总数 __le32 s_inodes_per_group;// 每个块组中inode的总数 ··············· __le16 s_magic;// 文件系统标志,ext2中为0xEF53 ··············· }; (2)块组描述符(每条描述符占32个字节): struct ext2_group_desc { __le32 bg_block_bitmap;// 块位图所在的第一个块的块ID __le32 bg_inode_bitmap;// inode位图所在的第一个块的块ID __le32 bg_inode_table;// inode表所在的第一个块的块ID __le16 bg_free_blocks_count;// 块组中未使用的块数 __le16 bg_free_inodes_count;// 块组中未使用的inode数 __le16 bg_pad; __le32 bg_reserved[3]; }; (3)inode表 inode表用于跟踪定位每个文件,包括位置、大小等(但不包括文件名),一个块组只有一个inode表。每一个inode都对应一个文件,一个inode的结构如下: struct ext2_inode { __le16 i_mode; // 文件格式和访问权限 __le16 i_uid;// 文件所有者ID的低16位 __le32 i_size;// 文件字节数 __le32 i_atime;// 文件上次被访问的时间 __le32 i_ctime;// 文件创建时间 __le32 i_mtime;// 文件被修改的时间 __le32 i_dtime;// 文件被删除的时间(如果存在则为0) __le16 i_gid;// 文件所有组ID的低16位 __le16 i_links_count;// 此inode被连接的次数 __le32 i_blocks;// 文件已使用和保留的总块数(以512B为单位) __le32 i_flags;// 此inode访问数据时ext2的实现方式 union { struct { __le32 l_i_reserved1;// 保留 } linux1; struct { __le32 h_i_translator;// “翻译者”标签 } hurd1; struct { __le32 m_i_reserved1;// 保留 } masix1; } osd1;// 操作系统相关数据 __le32 i_block[EXT2_N_BLOCKS];// 定位存储文件的块的数组,前12个为块号,第13个为一级间接块号,第14个为二级间接块号,第15个为三级间接块号 __le32 i_generation;// 用于NFS的文件版本 __le32 i_file_acl;// 包含扩展属性的块号,老版本中为0 __le32 i_dir_acl;// 表示文件的“High Size”,老版本中为0 __le32 i_faddr;// 文件最后一个段的地址 union { struct { __u8 l_i_frag;// 段号 __u8 l_i_fsize;// 段大小 __u16 i_pad1; __le16 l_i_uid_high;// 文件所有者ID的高16位 __le16 l_i_gid_high;// 文件所有组ID的高16位 __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag;// 段号 __u8 h_i_fsize;// 段大小 __le16 h_i_mode_high; __le16 h_i_uid_high;// 文件所有者ID的高16位 __le16 h_i_gid_high;// 文件所有组ID的高16位 __le32 h_i_author; } hurd2; struct { __u8 m_i_frag;// 段号 __u8 m_i_fsize;// 段大小 __u16 m_pad1; __u32 m_i_reserved2[2]; } masix2; } osd2; // 操作系统相关数据 }; (5)数据区的目录项结构: ExtX中根目录总是在inode表的第二项(在编程或者操作时可以充分利用这一特定信息)。 struct ext2_dir_entry_2 { __le32 inode; // 文件入口的inode号,0表示该项未使用 __le16 rec_len;// 目录项长度 __u8 name_len;// 文件名包含的字符数 __u8 file_type; // 文件类型 //以上四项共占8个字节(定长) char name[255];// 文件名(这是一个数组,根据name_len这一项确定长度,不定长) }; (6)Ext4: Ext4和Ext2、3的数据存储结构很不一样,它采用了一种叫extent的方式。所谓extent就是描述保存文件数据使用的连续物理块的一段范围。分为头结构、索引结构、叶子结构: //叶子 struct ext4_extent { __le32#160; ee_block;#160;#160;#160;#160;#160;#160; __le16#160; ee_len;#160;#160;#160;#160;#160;#160;#160;#160; __le16#160; ee_start_hi;#160;#160;#160; __le32#160; ee_start;#160;#160;#160;#160;#160;#160; }; //索引 struct ext4_extent_idx { __le32#160; ei_block;#160;#160;#160;#160;#160; __le32#160; ei_leaf;#160;#160;#160;#160;#160;#160; __le16#160; ei_leaf_hi;#160;#160;#160; __u16#160;#160; ei_unused; }; #160; //头结构 struct ext4_extent_header { __le16#160; eh_magic;#160;#160;#160;#160;#160;#160; __le16#160; eh_entries;#160;#160;#160; __le16#160; eh_max;#160;#160;#160;#160;#160;#160;#160;#160; __le16#160; eh_depth;#160;#160;#160;#160;#160;#160; __le32#160; eh_generation;#160; }; 每个 extent 都是一个 ext4_extent 类型的结构,大小为 12 字节。每个 ext4_extent 结构可以表示该文件从 ee_block 开始的 ee_len 个数据块,它们在磁盘上的位置是从 ee_start_hi<<32 ee_start 开始,到 ee_start_hi<<32 ee_start ee_len – 1 结束,全部都是连续的。 Ext4 文件系统的设计者们采用了一棵extent树结构,其布局如下图所示: 在 extent 树中,节点一共有两类:叶子节点和索引节点。保存文件数据的磁盘块信息全部记录在叶子节点中;而索引节点中则存储了叶子节点的位置和相对顺序。不管是叶子节点还是索引节点,最开始的 12 个字节总是一个 ext4_extent_header 结构,用来标识该数据块中有效项(ext4_extent 或 ext4_extent_idx 结构)的个数(eh_entries 域的值),其中 eh_depth 域用来表示它在 extent 树中的位置:对于叶子节点来说,该值为 0,之上每层索引节点依次加 1。extent 树的根节点保存在索引节点结构中的 i_block 域中,我们知道它是一个大小为 60 字节的数组,最多可以保存一个 ext4_extent_header 结构以及 4 个 ext4_extent 结构。对于小文件来说,只需要一次寻址就可以获得保存文件数据块的位置;而超出此限制的文件(例如很大的文件、碎片非常多的文件以及稀疏文件)只能通过遍历 extent 树来获得数据块的位置。 为了支持更大的文件系统,Ext4 决定采用48位的块号取代 Ext2、3原来的32位块号,并采用extent映射来取代ext2、3所采用的间接数据块映射的方法。其实Ext4的节点表、块位图、目录项等原理、结构基本一样,主要包括超级块、组描述符和节点的结构的区别如下: Ext4超级块: struct ext4_super_block { __le32#160; s_inodes_count;#160; __le32#160; s_blocks_count;#160;#160; // 文件系统中块的总数#160;(低32位)#160; __le32#160; s_r_blocks_count;#160; #160;//保留块的总数(低32位) __le32#160; s_free_blocks_count;#160;#160; //未使用块的总数(低32位)#160; __le32#160; s_free_inodes_count;#160;#160;#160; __le32#160; s_first_data_block;#160;#160;#160; __le32#160; s_log_block_size;#160;#160;#160;#160; ······· /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */ #160; __le32#160; s_blocks_count_hi;#160;#160;#160;#160; // 文件系统中块的总数#160;(高32位)#160;#160; __le32#160; s_r_blocks_count_hi;#160; //保留块的总数(高32位) __le32#160; s_free_blocks_count_hi; //未使用块的总数(高32位)#160; … }; 在 ext4_super_block 结构中,增加了3个与此相关的字段:s_blocks_count_hi,s_r_blocks_count_hi,s_free_blocks_count_hi,表示s_blocks_count、s_r_blocks_count、s_free_blocks_count 的高 32 位的值,将它们扩充到 64 位。 Ext4块组描述符: struct ext4_group_desc{ #160;__le32#160; bg_block_bitmap;#160;#160;#160; //块位图(低32位) __le32#160; bg_inode_bitmap;#160;#160;#160; #160;//节点位图(低32位)#160;#160;#160;#160; __le32#160; bg_inode_table;#160;#160;#160; //节点表(低32位)#160;#160; #160; #160;···················· __le32#160; bg_block_bitmap_hi; //块位图(高32位)#160;#160; __le32#160; bg_inode_bitmap_hi;//节点位图(高32位) __le32#160; bg_inode_table_hi;//节点表(高32位) }; Ext4节点: 与Ext2、3不同之处在于最后添加了 5 个与时间有关的字段,这是为了提高时间戳的精度。在 Ext2/Ext3 文件系统中,时间戳的精度只能达到秒级。随着硬件性能的提升,这种精度已经无法区分在同一秒中创建的文件的时间戳差异,这对于对精度要求很高的程序来说是无法接受的。在 Ext4 文件系统中,通过扩充索引节点结构解决了这个问题,可以实现纳秒级的精度。最后两个新增字段 i_crtime 和 i_crtime_extra 用来表示文件的创建时间,这可以用来满足某些应用程序的需求。 struct ext4_inode { __le16#160; i_mode;#160;#160;#160;#160;#160;#160;#160; __le16#160; i_uid;#160;#160;#160;#160;#160;#160;#160;#160;#160; __le32#160; i_size;#160;#160;#160;#160;#160;#160; __le32#160; i_atime;#160;#160;#160;#160;#160;#160; __le32#160; i_ctime;#160;#160;#160;#160;#160;#160; __le32#160; i_mtime;#160;#160;#160;#160;#160; __le32#160; i_dtime;#160;#160;#160;#160;#160; __le16#160; i_gid;#160;#160;#160;#160;#160;#160;#160;#160;#160; __le16#160; i_links_count;#160; __le32#160; i_blocks;#160;#160;#160;#160;#160; __le32#160; i_flags;#160;#160;#160;#160;#160;#160; … __le32#160; i_block[EXT4_N_BLOCKS];//还是那60个字节 __le32#160; i_generation;#160;#160; __le32#160; i_file_acl;#160;#160;#160;#160; __le32#160; i_dir_acl;#160;#160;#160;#160;#160; __le32#160; i_faddr;#160;#160;#160;#160;#160;#160; … __le16#160; i_extra_isize; __le16#160; i_pad1; #160;__le32#160; i_ctime_extra;#160; __le32#160; i_mtime_extra; __le32#160; i_atime_extra;#160; __le32#160; i_crtime;#160;#160;#160;#160;#160; __le32#160; i_crtime_extra; #160; }; 三、注意事项: (1)在对像/dev/sdaX进行读取操作时应该注意偏移量问题,lseek中那个offset是off_t型,当它是32位时,你的偏移量很大的时候就会放不下,所以应该加 #define _FILE_OFFSET_BITS 64 (2)在对Ext4文件系统进行操作时应该注意它的(/)要用struct ext4_inode去取,但是像根下的usr、var、home这些子目录下的内容就要用extent的头、叶子、索引三个结构去取; 主要参考资料: (1)本文中涉及的相关数据结构体均摘抄于Redhat下/usr/include下头文件 (2)数据重现文件系统原理精解与数据恢复最佳实践.(马林) (3)ext4 存储原理及特性-如何恢复 Linux 上删除的文件_中文DY豆_cn_dydou_cn.htm(冯锐)
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~