Ext2文件系统主要数据结构

Ext2文件系统的特点:
支持可变块长:创建文件系统时根据预期的文件平均长度来选择最佳的块大小
支持快速符号链接:短路径名的符号链接直接存放在inode中
精巧复杂的文件更新策略使系统崩溃的影响减到最少
支持不可变(immutable)的文件(不能修改、删除和更名)和仅追加(append-only)的文件

硬盘的块组和块组结构
Ext2文件系统主要数据结构

数据块寻址采取分级间接寻址,如下图示:
Ext2文件系统主要数据结构

数据结构
①超级块

struct ext2_super_block {
    __le32  s_inodes_count;     /* 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; /* 第一个数据块*/
    __le32  s_log_block_size;   /* 块长度 实际长度=1<<(s_log_block_size+10) */
    __le32  s_log_frag_size;    /* 碎片长度 */
    __le32  s_blocks_per_group; /* # 每个块组包含的块数 */
    __le32  s_frags_per_group;  /* # 每个块组包含的碎片 */
    __le32  s_inodes_per_group; /* # 每个块组包含的inode数目 */
    __le32  s_mtime;        /* Mount时间 */
    __le32  s_wtime;        /* 写入时间 */
    __le16  s_mnt_count;        /* Mount计数*/
    __le16  s_max_mnt_count;    /* 最大mount计数*/
    __le16  s_magic;        /* 魔数,标记文件系统类型 */
    __le16  s_state;        /* 文件系统状态 */
    __le16  s_errors;       /* 检测到错误时的状态 */
    __le16  s_minor_rev_level;  /* 副修订号*/
    __le32  s_lastcheck;        /* 上一次检查时间 */
    __le32  s_checkinterval;    /* 两次检查允许间隔的最长时间 */
    __le32  s_creator_os;       /* 创建文件系统的OS */
    __le32  s_rev_level;        /* 修订号 */
    __le16  s_def_resuid;       /* 能够使用保留块的默认UID */
    __le16  s_def_resgid;       /* 能够使用保留块的默认GID */
    /*
     * 这些字段只适用于EXT2_DYNAMIC_REV 超级块
     * 请注意:兼容特性集与不兼容特性集的差别在于;如果不兼容特性中某个置位的比特位内核
     * 不了解则应该拒绝装载该文件系统
     * 
     * e2fsck的要求更为严格。如果它不了解某个特性,不管是不是兼容特性
     * 它都必须放弃工作,而不是去尽力弄乱不了解的东西
     */
    __le32  s_first_ino;        /* 第一个非保留的inode */
    __le16   s_inode_size;      /* inode结构的长度 */
    __le16  s_block_group_nr;   /* 当前超级块所在的块组编好*/
    __le32  s_feature_compat;   /* compatible feature set 兼容特性集*/
    __le32  s_feature_incompat;     /* incompatible feature set 不兼容特性集 */
    __le32  s_feature_ro_compat;    /* readonly-compatible feature set 只读兼容特性集 */
    __u8    s_uuid[16];     /* 128-bit uuid for volume 卷的128位uuid */
    char    s_volume_name[16];  /* volume name 卷名*/
    char    s_last_mounted[64];     /* directory where last mounted 上一次装载目录 */
    __le32  s_algorithm_usage_bitmap; /* For compression  用于压缩*/
    /*
     * 性能提示。仅当设置了EXT2_COMPAT_PREALLOC flag标志,才能进行目录的已分配
     */
    __u8    s_prealloc_blocks;  /* Nr of blocks to try to preallocate 试图预分配的块数*/
    __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs 试图为目录预分配的块数 */
    __u16   s_padding1;
    /*
     * 如果设置了EXT3_FEATURE_COMPAT_HAS_JOURNAL,日志支持才是有效的
     */
    __u8    s_journal_uuid[16]; /* uuid of journal superblock */
    __u32   s_journal_inum;     /* inode number of journal file */
    __u32   s_journal_dev;      /* device number of journal file */
    __u32   s_last_orphan;      /* start of list of inodes to delete */
    __u32   s_hash_seed[4];     /* HTREE hash seed */
    __u8    s_def_hash_version; /* Default hash version to use */
    __u8    s_reserved_char_pad;
    __u16   s_reserved_word_pad;
    __le32  s_default_mount_opts;
    __le32  s_first_meta_bg;    /* First metablock block group */
    __u32   s_reserved[190];    /* Padding to the end of the block 填充字节,补齐到块结尾 */
};

s_def_resuid\s_def_resgid,指定了一个系统用户的用户ID和组ID,对该用户已经专门分配了一定数目的块由s_r_blocks_count表示,这些块其他用户无法使用。默认UID和GID为0,对应root用户,主要是为了在普通用户无法写磁盘是保证root用户可以登录并管理
s_state、s_lastcheck、s_checkinterval维护文件系统的一致性检查

② 块组描述符

{
		__le32 bg_block_bitmap; /* Blocks bitmap block 块位图快 */
		__le32 bg_inode_bitmap; /* Inodes bitmap block inode位图块 */
		__le32 bg_inode_table; /* Inodes table block  inode表块*/
		__le16 bg_free_blocks_count; /* Free blocks count 空闲块数目*/
		__le16 bg_free_inodes_count; /* Free inodes count 空闲inode数目 */
		__le16 bg_used_dirs_count; /* Directories count 目录数目 */
		__le16 bg_pad;
		__le32 bg_reserved[3];
};

bg_block_bitmap引用的块不用来存储数据。其中每个bit表示当前块组中的一个数据块。置位表示对应块在使用,否则概括是可用的
bg_inode_bitmap类似bg_block_bitmap,描述块组中的所有inode

③ inode

struct ext2_inode {
         __le16  i_mode;         /* File mode 文件模式*/
         __le16  i_uid;          /* Low 16 bits of Owner Uid 所有者UID的低16为 */
         __le32  i_size;         /* Size in bytes 长度,按自己计算 */
         __le32  i_atime;        /* Access time 访问时间*/
         __le32  i_ctime;        /* Creation time 创建时间 */
         __le32  i_mtime;        /* Modification time 修改时间*/
         __le32  i_dtime;        /* Deletion Time 删除时间 */
         __le16  i_gid;          /* Low 16 bits of Group Id 组ID的低16位*/
         __le16  i_links_count;  /* Links count 链接计数 */
         __le32  i_blocks;       /* Blocks count 块数目*/
         __le32  i_flags;        /* File flags 文件标志*/
         union {
                 struct {
                         __le32  l_i_reserved1;
                 } linux1;
                 struct {
                         __le32  h_i_translator;
                 } hurd1;
                 struct {
                         __le32  m_i_reserved1;
                 } masix1;
         } osd1;                         /* OS dependent 1 */
         __le32  i_block[EXT2_N_BLOCKS];/* Pointers to blocks块指针(块号) */
         __le32  i_generation;   /* File version (for NFS) 文件版本 */
         __le32  i_file_acl;     /* File ACL 文件acl*/
         __le32  i_dir_acl;      /* Directory ACL 目录acl*/
         __le32  i_faddr;        /* Fragment address 碎片地址 */
         union {
                 struct {
                         __u8    l_i_frag;       /* Fragment number 碎片编号 */
                         __u8    l_i_fsize;      /* Fragment size 碎片长度*/
                         __u16   i_pad1;
                         __le16  l_i_uid_high;   /* these 2 fields    */
                         __le16  l_i_gid_high;   /* were reserved2[0] 此前定义为reserved2[0] */
                         __u32   l_i_reserved2;
                 } linux2;
                 struct {
                         __u8    h_i_frag;       /* Fragment number */
                         __u8    h_i_fsize;      /* Fragment size */
                         __le16  h_i_mode_high;
                         __le16  h_i_uid_high;
                         __le16  h_i_gid_high;
                         __le32  h_i_author;
                 } hurd2;
                 struct {
                         __u8    m_i_frag;       /* Fragment number */
                         __u8    m_i_fsize;      /* Fragment size */
                         __u16   m_pad1;
                         __u32   m_i_reserved2[2];
                 } masix2;
         } osd2;                         /* OS dependent 2 */
 };

当符号链接的目标路径长度小于60字节时,i_block[]数组直接用来存储链接目标路径名,当超过60字节时分配一个数据块来存储字符串

④ 目录项

structext2_dir_entry_2 {
		__le32 inode; /* Inode number 指针,指向目录项的inode*/
		__le16 rec_len; /* Directory entry length 目录项长度*/
		__u8 name_len; /* Name length */
		__u8 file_type;
		char name[EXT2_NAME_LEN]; /* File name */
};

rec_len目录项长度,是一个偏移量,表示从rec_len字段末尾到下一个rec_len字段末尾的偏移量,单位是字节

⑤内存数据结构ext2_sb_info

struct ext2_sb_info {
    unsigned long s_frag_size;  /* Size of a fragment in bytes 碎片长度,字节为单位 */
    unsigned long s_frags_per_block;/* Number of fragments per block 每块中的碎片数目*/
    unsigned long s_inodes_per_block;/* Number of inodes per block  每块中的inode数目*/
    unsigned long s_frags_per_group;/* Number of fragments in a group 每个块组中的碎片数目*/
    unsigned long s_blocks_per_group;/* Number of blocks in a group 块组中的块的数目 */
    unsigned long s_inodes_per_group;/* Number of inodes in a group 块组中的inode的数目*/
    unsigned long s_itb_per_group;  /* Number of inode table blocks per group 每个块组中用于inode table的块数 */
    unsigned long s_gdb_count;  /* Number of group descriptor blocks用于块描述符的块数 */
    unsigned long s_desc_per_block; /* Number of group descriptors per block 每块可以容纳的组描述符的数目*/
    unsigned long s_groups_count;   /* Number of groups in the fs 文件系统中块组的数目 */
    unsigned long s_overhead_last;  /* Last calculated overhead 上一次计算管理数据的开销*/
    unsigned long s_blocks_last;    /* Last seen block count 下一次计算的可用块数*/
    struct buffer_head * s_sbh; /* Buffer containing the super block 包含了超级块的缓冲区*/
    struct ext2_super_block * s_es; /* Pointer to the super block in the buffer 指向缓冲区中超级块的指针*/
    struct buffer_head ** s_group_desc;
    unsigned long  s_mount_opt;
    unsigned long s_sb_block;
    uid_t s_resuid;
    gid_t s_resgid;
    unsigned short s_mount_state;
    unsigned short s_pad;
    int s_addr_per_block_bits;
    int s_desc_per_block_bits;
    int s_inode_size;
    int s_first_ino;
    spinlock_t s_next_gen_lock;
    u32 s_next_generation;
    unsigned long s_dir_count;    /*ext2_count_dirs()函数*/
    u8 *s_debts;/*指向一个数组,数组项对应一个块组*/
    struct percpu_counter s_freeblocks_counter;
    struct percpu_counter s_freeinodes_counter;
    struct percpu_counter s_dirs_counter;
    struct blockgroup_lock *s_blockgroup_lock;
    /* root of the per fs reservation window tree */
    spinlock_t s_rsv_window_lock;
    struct rb_root s_rsv_window_root;
    struct ext2_reserve_window_node s_rsv_window_head;
};

s_sb_block:若超级块不是从默认的块1中读取,则该字段存储了读取超级块的块值
s_debts 指向一个数组,数组项对应一个块组

Ext2的预分配机制相关的数据结构

struct ext2_reserve_window {
	ext2_fsblk_t  _rsv_start; /*第一个预留的字节 */
	ext2_fsblk_t _rsv_end; /*最后一个预留的字节,或为0 */
};
struct ext2_inode_info {
		……
		struct ext2_block_alloc_info *i_block_alloc_info;
		……
}
struct ext2_sb_info {
		……
    spinlock_t s_rsv_window_lock;
    struct rb_root s_rsv_window_root;
    struct ext2_reserve_window_node s_rsv_window_head;
		……
}
struct ext2_reserve_window_node {
struct rb_node rsv_node;
__u32 rsv_goal_size;//预留窗口的预期长度
__u32 rsv_alloc_hit;//预分配的命中数
struct ext2_reserve_window rsv_window;//预留窗口
};
structext2_block_alloc_info {
		struct ext2_reserve_window_node rsv_window_node;
		__u32 last_alloc_logical_block;//上一次分配的块在文件中的相对块号
		ext2_fsblk_t last_alloc_physical_block;//块在设备上的物理块号
};

这些数据结构彼此嵌套,相互之间有着紧密的联系
Ext2文件系统主要数据结构