EXT4文件系统之ext4_fill_super()
一.概述
在磁盘挂载的时候文件系统需要从磁盘中读取超级块来填充内存中的结构,EXT4文件系统超级块的填充是由函数ext4_fill_super()来完成的。在EXT4文件系统中,磁盘上的超级块结构是与结构体structext4_super_block的定义是一致的,大小是1K,即1024个字节。顺便提下,EXT3文件系统超级块在磁盘上的大小也是1024个字节,EXT4扩展了EXT3的定义,EXT3只是占了1024个字节,有些字节没有定义,在EXT4中重新定义了,总的大小没有改变。
二.具体流程
在挂载文件系统的时候,读取磁盘上ext4_super_block结构的值,填充内存中ext4_sb_info的结构。
2.1读取超级块
sb_block= get_sb_block(&data);
//计算超级块所在的块,data数值来自于参数,当data为空值,则sb_block=1; data中可以指定超级块的块号,即定义”sb=3”,超级块在块3.
……
blocksize= sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
//初步计算块大小,最小为EXT4_MIN_BLOCK_SIZE,即1K
……
if(blocksize != EXT4_MIN_BLOCK_SIZE)
{//块大小不一致,需要重新计算
logical_sb_block= sb_block * EXT4_MIN_BLOCK_SIZE;
offset= do_div(logical_sb_block, blocksize);
}else
{
logical_sb_block= sb_block; //块大小一致,直接读取
}
……
if(!(bh = sb_bread(sb, logical_sb_block)))//从磁盘中读取logical_sb_block块
es= (struct ext4_super_block *) (bh->b_data + offset); //超级块数据
sbi->s_es= es; //超级块
/
logical_sb_block= sb_block * EXT4_MIN_BLOCK_SIZE;
offset= do_div(logical_sb_block, blocksize);
# define do_div(n,base) ({ \
uint32_t__base = (base); \
uint32_t__rem; \
__rem= ((uint64_t)(n)) % __base; \
(n)= ((uint64_t)(n)) / __base; \
__rem; \
})
即
logic_sb_block = (sb_block * EXT4_MIN_BLOCK_SIZE)/ blocksize;
offset= (sb_block * EXT4_MIN_BLOCK_SIZE) % blocksize;
如果块大小为1K,那logic_sb_block即为1,offset=0;超级块所在位置为块1;
如果块大小为4K,那logic_sb_block即为0,offset=1024;超姐块所在位置为块0,但是偏移为1024,两者比较,其实在磁盘的同一个位置上,与整个块组开头偏移1024个字节。
当超级块内容读取出来后,里面会有个参数指定磁盘上的块大小,如果块大小的值与本次读的时候的块不一致,会根据新的块大小来重新读一遍,但本质不变,超级块即1K大小的磁盘块数据。
2.2读取块组描述符
blocks_count= (ext4_blocks_count(es) -le32_to_cpu(es->s_first_data_block) +EXT4_BLOCKS_PER_GROUP(sb) - 1);
do_div(blocks_count,EXT4_BLOCKS_PER_GROUP(sb));//do_div上文已提到
块的总数/每个块组中的块数=块组个数,即blocks_count。
sbi->s_groups_count= blocks_count;
db_count= (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) /EXT4_DESC_PER_BLOCK(sb);
块组个数/一个块中包含的块组描述符的个数=块描述符需要的块的个数,即有多少个块来表示这些块组描述符
structbuffer_head **s_group_desc;
sbi->s_group_desc= ext4_kvmalloc(db_count *sizeof(struct buffer_head *),GFP_KERNEL);
分配块描述符的内存;
for (i = 0; i < db_count; i++)
{//循坏块的个数
block= descriptor_loc(sb, logical_sb_block, i);//块组描述符所在的块
sbi->s_group_desc[i]= sb_bread(sb, block); //读取块组描述符
}
static ext4_fsblk_t descriptor_loc(structsuper_block *sb,ext4_fsblk_t logical_sb_block, int nr)
{
structext4_sb_info *sbi = EXT4_SB(sb);
ext4_group_tbg, first_meta_bg;
inthas_super = 0;
first_meta_bg= le32_to_cpu(sbi->s_es->s_first_meta_bg);
if(!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) ||nr <first_meta_bg)
returnlogical_sb_block + nr + 1; //flex_bg模式,块组描述符在超级块的后面,从0到db_count,保存了磁盘上所有的块组描述符。
//下面是meta_bg的模式
bg= sbi->s_desc_per_block * nr; //一个meta_bg中块组的个数*第nr个meta_bg块组 ,一个meta_bg中块组的个数=一个块的大小/块组描述符的大小
if(ext4_bg_has_super(sb, bg))//根据是否有sparse_super的参数来确认是否有超级块
has_super= 1;
return(has_super + ext4_group_first_block_no(sb, bg));//根据sparse_super判断块组中是否有超级块。0,3,5,7的幂
}
static inline ext4_fsblk_t
ext4_group_first_block_no(structsuper_block *sb, ext4_group_t group_no)
{
return group_no * (ext4_fsblk_t)EXT4_BLOCKS_PER_GROUP(sb) +le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
//上文中一个meta_bg中块组的个数*nr*一个块组中的块数,nr即为循环偏移
}
2.3读取根节点
#define EXT4_ROOT_INO 2 /* Root inode */
root = ext4_iget(sb, EXT4_ROOT_INO);
根节点即为整个文件系统的入口,得到根目录或者根目录下的文件或者目录。