存储 频道

老鸟帮您分析ext2文件系统

  【IT168 技术】随着Linux 的 发展和开源优势 正在逐渐被广大用户接受 下面我介绍一下ext2文件系统的相关知识e,xt2文件系统是Linux系统中的标准文件系统,是通过对Minix的文件系统进行扩展而得到的,其存取文件的性能极好。

  在ext2文件系统中,文件由inode(包含有文件的所有信息)进行唯一标识。一个文件可能对应多个文件名,只有在所有文件名都被删除后,该文件才会被删除。此外,同一文件在磁盘中存放和被打开时所对应的inode是不同的,并由内核负责同步。

  ext2文件系统采用三级间接块来存储数据块指针,并以块(block,默认为1KB)为单位分配空间。其磁盘分配策略是尽可能将逻辑相邻的文件分配到磁盘上物理相邻的块中,并尽可能将碎片分配给尽量少的文件,以从全局上提高性能。ext2文件系统将同一目录下的文件(包括目录)尽可能的放在同一个块组中,但目录则分布在各个块组中以实现负载均衡。在扩展文件时,会尽量一次性扩展8个连续块给文件(以预留空间的形式实现)。

  一、磁盘组织

  在ext2文件系统中,所有元数据结构的大小均基于“块”,而不是“扇区”。块的大小随文件系统的大小而有所不同。而一定数量的块又组成一个块组,每个块组的起始部分有多种多样的描述该块组各种属性的元数据结构。ext2系统中对各个结构的定义都包含在原始码的include/linux/ext2_fs.h文件中。

  1.终极块

  每个ext2文件系统都必须包含一个终极块,其中存储了该文件系统的大量基本信息,包括块的大小、每块组中包含的块数等。同时,系统会对终极块进行备份,备份被存放在块组的第一个块中。终极块的起始位置为其所在分区的第1024个字节,占用1KB的空间,其结构如下:

  struct ext2_super_block {

  __le32 s_inodes_count; // ext2文件系统中inode的总数

  __le32 s_blocks_count; // 文件系统中块的总数

  __le32 s_r_blocks_count; // 保留块的总数

  __le32 s_free_blocks_count; // 未使用的块的总数(包括保留块)

  __le32 s_free_inodes_count; // 未使用的inode的总数

  __le32 s_first_data_block; // 块ID,在小于1KB的文件系统中为0,大于1KB的文件系统中为1

  __le32 s_log_block_size; // 用以计算块的大小(1024算术左移该值即为块大小)

  __le32 s_log_frag_size; // 用以计算段大小(为正则1024算术左移该值,否则右移)

  __le32 s_blocks_per_group; // 每个块组中块的总数

  __le32 s_frags_per_group; // 每个块组中段的总数

  __le32 s_inodes_per_group; // 每个块组中inode的总数

  __le32 s_mtime; // POSIX中定义的文件系统装载时间

  __le32 s_wtime; // POSIX中定义的文件系统最近被写入的时间

  __le16 s_mnt_count; // 最近一次完整校验后被装载的次数

  __le16 s_max_mnt_count; // 在进行完整校验前还能被装载的次数

  __le16 s_magic; // 文件系统标志,ext2文件系统中为0xEF53

  __le16 s_state; // 文件系统的状态

  __le16 s_errors; // 文件系统发生错误时驱动程式应该执行的操作

  __le16 s_minor_rev_level; // 局部修订级别

  __le32 s_lastcheck; // POSIX中定义的文件系统最近一次检查的时间

  __le32 s_checkinterval; // POSIX中定义的文件系统最近检查的最大时间间隔

  __le32 s_creator_os; // 生成该ext2文件系统的操作系统

  __le32 s_rev_level; // 修订级别

  __le16 s_def_resuid; // 报留块的默认用户ID

  __le16 s_def_resgid; // 保留块的默认组ID

  // 仅用于使用动态inode大小的修订版(EXT2_DYNAMIC_REV)

  __le32 s_first_ino; // 标准文件的第一个可用inode的索引(非动态为11)

  __le16 s_inode_size; // inode结构的大小(非动态为128)

  __le16 s_block_group_nr; // 保存此终极块的块组号

  __le32 s_feature_compat; // 兼容特性掩码

  __le32 s_feature_incompat; // 不兼容特性掩码

  __le32 s_feature_ro_compat; // 只读特性掩码

  __u8 s_uuid[16]; // 卷ID,应尽可能使每个ext2文件系统的格式唯一

  char s_volume_name[16]; // 卷名(只能为ISO-Latin-1字符集,以’\0’结束)

  char s_last_mounted[64]; // 最近被安装的目录

  __le32 s_algorithm_usage_bitmap; // 文件系统采用的压缩算法

  // 仅在EXT2_COMPAT_PREALLOC标志被设置时有效

  __u8 s_prealloc_blocks; // 预分配的块数

  __u8 s_prealloc_dir_blocks; // 给目录预分配的块数

  __u16 s_padding1;

  // 仅在EXT3_FEATURE_COMPAT_HAS_JOURNAL标志被设置时有效,用以支持日志

  __u8 s_journal_uuid[16]; // 日志终极块的卷ID

  __u32 s_journal_inum; // 日志文件的inode数目

  __u32 s_journal_dev; // 日志文件的设备数

  __u32 s_last_orphan; // 要删除的inode列表的起始位置

  __u32 s_hash_seed[4]; // HTREE散列种子

  __u8 s_def_hash_version; // 默认使用的散列函数

  __u8 s_reserved_char_pad;

  __u16 s_reserved_word_pad;

  __le32 s_default_mount_opts;

  __le32 s_first_meta_bg; // 块组的第一个元块

  __u32 s_reserved[190];

  };

  2.块组描述符

  一个块组描述符用以描述一个块组的属性。块组描述符组由若干块组描述符组成,描述了文件系统中所有块组的属性,存放于终极块所在块的下一个块中。一个块组描述符的结构如下:

  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_used_dirs_count; // 块组分配的目录的inode数

  __le16 bg_pad;

  __le32 bg_reserved[3];

  };

  3.块位图和inode位图

  块位图和inode位图的每一位分别指出块组中对应的那个块或inode是否被使用。

  4.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.数据块

  数据块中存放文件的内容,包括目录表、扩展属性、符号链接等。

  二、目录结构

  在ext2文件系统中,目录是作为文件存储的。根目录总是在inode表的第二项,而其子目录则在根目录文件的内容中定义。目录项在include/linux/ext2_fs.h文件中定义,其结构如下:

  struct ext2_dir_entry_2 {

  __le32 inode; // 文件入口的inode号,0表示该项未使用

  __le16 rec_len; // 目录项长度

  __u8 name_len; // 文件名包含的字符数

  __u8 file_type; // 文件类型

  char name[255]; // 文件名

  };

  三、文件扩展属性

  文件的属性大多数是位于该文件的inode结构中的标准属性,也还包含其他一些扩展属性(于系统中所有的inode相关,通常用于增加额外的功能),在fs/ext2/xattr.h文件中定义。

  inode的i_file_acl字段中保存扩展属性的块的块号。属性头部项位于属性块的起始位置,其后为属性入口项,而属性值能根据属性入口项找到所在位置。

  1.属性头部项

  struct ext2_xattr_header {

  __le32 h_magic; // 标识码,为0xEA020000

  __le32 h_refcount; // 属性块被链接的数目

  __le32 h_blocks; // 用于扩展属性的块数

  __le32 h_hash; // 所有属性的哈希值

  __u32 h_reserved[4];

  };

  2.属性入口项

  struct ext2_xattr_entry {

  __u8 e_name_len; // 属性名长度

  __u8 e_name_index; // 属性名索引

  __le16 e_value_offs; // 属性值在值块中的偏移量

  __le32 e_value_block; // 保存值的块的块号

  __le32 e_value_size; // 属性值长度

  __le32 e_hash; // 属性名和值的哈希值

  char e_name[0]; // 属性名

  };

0
相关文章