Ext2文件系统主要数据结构
Ext2文件系统的特点:
支持可变块长:创建文件系统时根据预期的文件平均长度来选择最佳的块大小
支持快速符号链接:短路径名的符号链接直接存放在inode中
精巧复杂的文件更新策略使系统崩溃的影响减到最少
支持不可变(immutable)的文件(不能修改、删除和更名)和仅追加(append-only)的文件
硬盘的块组和块组结构
数据块寻址采取分级间接寻址,如下图示:
数据结构
①超级块
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;//块在设备上的物理块号
};
这些数据结构彼此嵌套,相互之间有着紧密的联系