今天我们来认识一下superblock,inode,block,group,group descriptor,block bitmap,inode table这些家伙。为什么在上一篇博文开篇没详细介绍这些概念呢,因为任何关于文件系统的文章还是书籍一开始都是先说概念、说理论,让人一直有种雾里看花的感觉。纸上得来终觉浅,事必躬亲才印象深,所以我们以一块实际硬盘为例(当然是虚拟出来的,呵呵)来向大家展示了一下文件系统在存储介质上的布局情况,先让大家对其有个比较直观的认识,然后再逐一对它们进行解释和说明,印象会更深刻些。咱不否认理论的重要性,秉承着“理论指导实践,以实践加深对理论的认识”的宗旨来一步一步入文件系统的乐园。
superblock:
这个东西确实很重要,前面我们已经见识过。为此,文件系统还特意精挑细选的找了N多后备Group,在这些Group中都存有superblock的副本,你就知道它有多重要了。说白了,superblock 的作用就是记录文件系统的类型、block大小、block总数、inode大小、inode总数、group的总数等等。
group descriptors:
千万不要以为这就是一个组描述符,看到descriptor后面加了个s就知道这是N多描述符的集合。确实,这是文件系统中所有group的描述符所构成的一个数组,它的结构定义在include/linux/ext2_fs.h中:
/* * Structure of a blocks group descriptor */ struct ext2_group_desc { __le32 bg_block_bitmap; /* group中block bitmap所在的第一个block号 */ __le32 bg_inode_bitmap; /* group中inode bitmap 所在的第一个block号 */ __le32 bg_inode_table; /* group中inodes table 所在的第一个block号 */ __le16 bg_free_blocks_count; /* group中空闲的block总数 */ __le16 bg_free_inodes_count; /* group中空闲的inode总数*/ __le16 bg_used_dirs_count; /* 目录数 */ __le16 bg_pad; __le32 bg_reserved[3]; }; |
下面的程序可以帮助了解一下 /dev/hdd1 中所有 group 的 descriptor 的详情:
[root@localhost cu]# cat grp_dsp.c #include #include #include #include #include #include #include
#define B_LEN 32 //一个struct ext2_group_desc{}占固定32字节
int main(int argc,char** argv){ char buf[B_LEN] = {0}; int i=0,fd = -1; struct ext2_group_desc gd; memset(&gd,0,B_LEN);
if(-1 == (fd=open(argv[1],O_RDONLY,0777))){ printf("open file error!\n"); return 1; }
while(i<64){ //因为我已经知道了/dev/hdd1中只有64个group if(-1 == read(fd,buf,B_LEN)){ printf("read error!\n"); close(fd); return 1; } memcpy((char*)&gd,buf,B_LEN); printf("========== Group %d: ==========\n",i); printf("Blocks bitmap block %ld \n",gd.bg_block_bitmap); printf("Inodes bitmap block %ld \n",gd.bg_inode_bitmap); printf("Inodes table block %ld \n",gd.bg_inode_table); printf("Free blocks count %d \n",gd.bg_free_blocks_count); printf("Free inodes count %d \n",gd.bg_free_inodes_count); printf("Directories count %d \n",gd.bg_used_dirs_count);
memset(buf,0,B_LEN); i++; }
close(fd); return 0; } |
运行结果和dumpe2fs /dev/hdd1的输出对比如下:
其中,文件gp0decp是由命令“dd if=/dev/hdd1 of=./gp0decp bs=4096 skip=1 count=1”生成。每个group descriptor里记录了该group中的inode table的起始block号,因为inode table有可能会占用连续的多个block;空闲的block、inode数等等。
block bitmap:
在文件系统中每个对象都有一个对应的inode节点(这句话有些不太准确,因为符号链接和它的目标文件共用一个inode),里存储了一个对象(文件或目录)的信息有权限、所占字节数、创建时间、修改时间、链接数、属主ID、组ID,如果是文件的话还会包含文件内容占用的block总数以及block号。inode是从1编号,这一点不同于block,需要格外注意。另外,/dev/hdd1是新挂载的硬盘,格式化成ext2后并没有任何数据,只有一个lost+found目录。接下来我们用命令“dd if=/dev/hdd1 of=./gp0 bs=4096 count=32768”将Group0里的所有数据提取出来。
前面已经了解了Group0的一些基本信息如下:
Group 0: (Blocks 0-32767) Primary superblock at 0, Group descriptors at 1-1 Reserved GDT blocks at 2-512 Block bitmap at 513 (+513), Inode bitmap at 514 (+514) Inode table at 515-1026 (+515) 31739 free blocks, 16374 free inodes, 1 directories #包含一个目录 Free blocks: 1028-1031, 1033-32767 #一共有31739个空闲block Free inodes: 11-16384 #一共有16374个空闲inode |
发现block bitmap的前128字节和第129字节的低4位都为1,说明发现Group0中前128×8+4=1028个block,即block0~block1027都已被使用了。第129字节的高4位为0,表示block1028~block1031四个block是空闲的;第130字节的最低位是1,说明block1032被占用了;从block1033~block32767的block bitmap都是0,所以这些block都是空闲的,和上表输出的结果一致。
inode bitmap:
和block bitmap类似,每个比特表示相应的inode是否被使用。Group0的inode bitmap如下:
![](/attachment/201301/15/23069658_13582620187A8v.jpg)
/dev/hdd1里inode总数为1048576,要被均分到64个Group里,所以每个Group中都包含了16384个inode。要表示每个Group中16384个inode,inode bitmap总共需要使用2048(16384/8)字节。inode bitmap本身就占据了一个block,所以它只用到了该block中的前2048个字节,剩下的2048字节都被填充成1,如上图所示。
我们可以看到Group0中的inode bitmap前两个字节分别是ff和03,说明Group0里的前11个inode已经被使用了。其中前10个inode被ext2预留起来,第11个inode就是lost+found目录,如下:
![](/attachment/201301/15/23069658_1358262047E8ma.jpg)
inode table:
那么每个Group中的所有inode到底存放在哪里呢?答案就是inode table。它是每个Group中所有inode的聚合地。
因为一个inode占128字节,所以每个Group里的所有inode共占16384×128=2097152字节,总共消耗了512个block。Group的group descriptor里记录了inode table的所占block的起始号,所以就可以唯一确定每个Group里inode table所在的block的起始号和结束号了。inode的结构如下:
这里我们主要关注的其中的数据block指针部分。前12个block指针直接指向了存有数据的block号;第13个block指针所指向的block中存储的并不是数据而是由其他block号,这些block号所对应的block里存储的才是真正的数据,即所谓的两级block指针;第14个block为三级block指针;第15个block为四级block指针。最后效果图如下:
![](/attachment/201301/15/23069658_1358262471Ops2.jpg)
一个block为4096字节,每个块指针4字节,所以一个block里最多可以容纳4096/4=1024个block指针,我们可以计算出一个inode最大能表示的单个文件的最大容量如下:
直接block指针(字节) | 两级block指针(字节) | 三级block指针(字节) | 四级block指针(字节) | 单个文件的最大容量(字节) |
12×409 | 4096/4×4096 | 40962/4×4096 | 40963/4×4096 | 4TB |
如下表所示:
block大小(字节) | 单个文件容量(字节) |
1024 | 17247240192字节(16GB) |
2048 | 275415826432字节(256GB) |
4096 | 4402345672704字节(4TB) |
完。
wjlkoorey2582013-01-17 22:25:08
shangbaogen:这样的文章必须顶,文章图文并茂,通俗易懂,相当好,你和Bean_lee兄让我学到了很多东西,感谢~
和大家分享学习过程,本身也是一件快乐的事儿。希望你也感同身受,呵呵
回复 | 举报