2021SC@SDUSC
在文件目录下有名为ext2.h的头文件,提供了许多该文件系统使用的数据类型和方法,所以先分析该文件以便了解整个文件系统的结构。
文件开始定义了与块的分配和查找有关的如下两个数据类型:
typedef int ext2_grpblk_t; //记录块组的块偏移量
typedef unsigned long ext2_fsblk_t; //记录文件系统范围的块数
随后是以下三个数据结构,与块的预留机制有关。早期的ext2其实使用的是预分配机制,其目的是为一个文件分配块是,考虑到文件增长的需求,为该文件提前多分配出一些预留块,那么下次为该文件分配块时,就可以使用这些预留块了,不仅减少查找空闲块的时间,也使得该文件可以连续分配。但是预分配在磁盘块位图上已经对块做了修改,所以从文件系统使用磁盘的角度来看,这些预分配的磁盘块与已分配的磁盘块是一样的,而预留块的信息是存放在ext2_inode_info结构中的,该结构只是在内存中存在,那么当文件关闭后,就会造成存储信息不一致,即未使用的块但却被标记为已使用,造成空间的浪费。所以为了解决这个问题,在后续的发展中增加了日志功能,目的是将文件系统一致性的关键信息例如如超级块、块组描述符块,inode位图块、磁盘块位图块等及时地写到磁盘的日志空间中,便于系统崩溃后的恢复以及比对系统运行过程中变化的信息。随后又发展出了块预留机制,该机制所有操作都是在内存中进行的,不会修改块位图。所以预留机制和预分配机制的本质区别在于是否会对磁盘信息进行操作。
struct ext2_reserve_window {
ext2_fsblk_t _rsv_start; /* First byte reserved */
ext2_fsblk_t _rsv_end; /* Last byte reserved or 0 */
};
该结构定义了一个预留区间,即预留块的起始块数和结束块数
struct ext2_reserve_window_node {
struct rb_node rsv_node;
__u32 rsv_goal_size;
__u32 rsv_alloc_hit;
struct ext2_reserve_window rsv_window;
};
该结构是预留窗口节点,定义了块预留区间的组织方式,即红黑树(Red Black Tree),通过红黑树的数据结构rb_node与预留区间的数据结构ext2_reserve_window 封装在一起,便于快速查找。其中rsv_goal_size是预留区间的长度, rsv_alloc_hit是预分配的命中次数。
struct ext2_block_alloc_info {
struct ext2_reserve_window_node rsv_window_node;
__u32 last_alloc_logical_block;
ext2_fsblk_t last_alloc_physical_block;
};
该结构定义了块分配信息,包括树的索引节点,最近分配的逻辑块号和物理块号。其中逻辑块号用来检查文件增长的分配请求,物理块号用来当检测到文件增长的请求时进行分配。
接下来的结构体名为ext2的超级块信息:
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 */
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 */
unsigned long s_itb_per_group; /* Number of inode table blocks per group */
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;
kuid_t s_resuid;
kgid_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;
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;
spinlock_t s_lock;
struct mb_cache *s_ea_block_cache;
struct dax_device *s_daxdev;
};
超级块描述整个分区的文件系统信息,包括碎片的大小、每个块有多少碎片、每个块组有多少块和节点等等。以及每个预留窗口节点数的根节点即s_rsv_window_head。
以下则定义了用于文件扩展的预留数据块信息,包括块数、块的大小、未名中数。
#define EXT2_DEFAULT_RESERVE_BLOCKS 8
#define EXT2_MAX_RESERVE_BLOCKS 1027
#define EXT2_RESERVE_WINDOW_NOT_ALLOCATED 0
以下定义了特殊的块的号码,例如坏块是1。
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
上次提到文件系统会将整个分区分成多个块组,以下结构为块组描述符,描述了每个块组的信息,即块和节点的使用情况、空闲的块数和节点数等等。
struct ext2_group_desc
{
__le32 bg_block_bitmap; /* Blocks bitmap block */
__le32 bg_inode_bitmap; /* Inodes bitmap block */
__le32 bg_inode_table; /* Inodes table block */
__le16 bg_free_blocks_count; /* Free blocks count */
__le16 bg_free_inodes_count; /* Free inodes count */
__le16 bg_used_dirs_count; /* Directories count */
__le16 bg_pad;
__le32 bg_reserved[3];
};
这四行是用来管理块组描述符的,从名称上能看出是每个快组的块数、节点数等。
#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
#define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block)
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
#define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits)
这三行是定义了某些特殊的数据块的数量,与块的读取有关。
#define EXT2_NDIR_BLOCKS 12 //直接块数量
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS //一级间接块数量
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)//二级间接块数量
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
这些是用户对文件进行操作时的标志符,对应文件的具体读写。
/*
* Inode flags (GETFLAGS/SETFLAGS)
*/
#define EXT2_SECRM_FL FS_SECRM_FL /* Secure deletion */
#define EXT2_UNRM_FL FS_UNRM_FL /* Undelete */
#define EXT2_COMPR_FL FS_COMPR_FL /* Compress file */
#define EXT2_SYNC_FL FS_SYNC_FL /* Synchronous updates */
#define EXT2_IMMUTABLE_FL FS_IMMUTABLE_FL /* Immutable file */
#define EXT2_APPEND_FL FS_APPEND_FL /* writes to file may only append */
#define EXT2_NODUMP_FL FS_NODUMP_FL /* do not dump file */
#define EXT2_NOATIME_FL FS_NOATIME_FL /* do not update atime */
/* Reserved for compression usage... */
#define EXT2_DIRTY_FL FS_DIRTY_FL
#define EXT2_COMPRBLK_FL FS_COMPRBLK_FL /* One or more compressed clusters */
#define EXT2_NOCOMP_FL FS_NOCOMP_FL /* Don't compress */
#define EXT2_ECOMPR_FL FS_ECOMPR_FL /* Compression error */
/* End compression flags --- maybe not all used */
#define EXT2_BTREE_FL FS_BTREE_FL /* btree format dir */
#define EXT2_INDEX_FL FS_INDEX_FL /* hash-indexed directory */
#define EXT2_IMAGIC_FL FS_IMAGIC_FL /* AFS directory */
#define EXT2_JOURNAL_DATA_FL FS_JOURNAL_DATA_FL /* Reserved for ext3 */
#define EXT2_NOTAIL_FL FS_NOTAIL_FL /* file tail should not be merged */
#define EXT2_DIRSYNC_FL FS_DIRSYNC_FL /* dirsync behaviour (directories only) */
#define EXT2_TOPDIR_FL FS_TOPDIR_FL /* Top of directory hierarchies*/
#define EXT2_RESERVED_FL FS_RESERVED_FL /* reserved for ext2 lib */
#define EXT2_FL_USER_VISIBLE FS_FL_USER_VISIBLE /* User visible flags */
#define EXT2_FL_USER_MODIFIABLE FS_FL_USER_MODIFIABLE /* User modifiable flags */