mkfs
命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息。下图是一个磁盘分区格式化成ext2文件系统后的存储布局。
文件系统中存储的最小单位是块(Block) ,一个块究竟多大是在格式化时确定的,例如mke2fs
的-b
选项可以设定块大小为1024、2048或4096字节。而上图中启动块(Boot Block) 的大小是确定的,就是1KB,启动块是由PC标准规定的,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。启动块之后才是ext2文件系统的开始,ext2文件系统将整个分区划成若干个同样大小的块组(Block Group) ,每个块组都由以下部分组成。
dd if=/dev/zero of=fs count=256 bs=4k //拷贝1M文件当设备
mke2fs fs
dumpe2fs fs
1为超级块
2为GDT
3-5为保留GDT
6 为块位图
7为节点位图
8-23为节点表,占16个块,每个节点128字节 ,128个节点共128*128=16k,即16个块
每分配一个inode ,对应要分配8k字节数据
37-1023为数据块
Filesystem UUID: 8e1f3b7a-4d1f-41dc-8928-526e43b2fd74
Filesystem magic number: 0xEF53
Filesystem features: resize_inode dir_index filetype sparse_super
Filesystem flags: signed directory hash
Default mount options: (none)
Filesystem state: clean
Filesystem OS type: Linux
Inode count: 128 //128个inode,每个8k大小 共1M
Block count: 1024 块数量1024个,每个inode 8个块
Reserved block count: 51
Free blocks: 986
Free inodes: 117
First block: 1
Block size: 1024 //每个块大小 1K字节
Fragment size: 1024
Reserved GDT blocks: 3 / /block 3-5为保留 ,block2为GDT
Blocks per group: 8192
Fragments per group: 8192
Inodes per group: 128
Inode blocks per group: 16 //每组16个块,
Filesystem created: Sun Dec 16 14:56:59 2007
Last mount time: n/a
Last write time: Sun Dec 16 14:56:59 2007
Mount count: 0
Maximum mount count: 30
Last checked: Sun Dec 16 14:56:59 2007
Check interval: 15552000 (6 months)
Next check after: Fri Jun 13 14:56:59 2008
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 128
Default directory hash: tea
Directory Hash Seed: 6d0e58bd-b9db-41ae-92b3-4563a02a5981
Group 0: (Blocks 1-1023)
Primary superblock at 1, Group descriptors at 2-2
Reserved GDT blocks at 3-5
Block bitmap at 6 (+5), Inode bitmap at 7 (+6)
Inode table at 8-23 (+7)
986 free blocks, 117 free inodes, 2 directories
Free blocks: 38-1023
Free inodes: 12-128
-
描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次
mount
的时间等等。超级块在每个块组的开头都有一份拷贝。 -
图 29.3. 超级块
块组描述符表(GDT,Group Descriptor Table)
由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符(Group Descriptor)
存储一个块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。通常内核只用到第0个块组中的拷贝,当执行
-
一个块组中的块是这样利用的:数据块存储所有文件的数据,比如某个分区的块大小是1024字节,某个文件是2049字节,那么就需要三个数据块来存,即使第三个块只存了一个字节也需要占用一个整块;超级块、块组描述符表、块位图、inode位图、inode表这几部分存储该块组的描述信息。那么如何知道哪些块已经用来存储文件数据或其它描述信息,哪些块仍然空闲可用呢?块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个bit代表本块组中的一个块,这个bit为1表示该块已用,这个bit为0表示该块空闲可用。
为什么用
df
命令统计整个磁盘的已用空间非常快呢?因为只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du
命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。与此相联系的另一个问题是:在格式化一个分区时究竟会划出多少个块组呢?主要的限制在于块位图本身必须只占一个块。用
mke2fs
格式化时默认块大小是1024字节,可以用-b
参数指定块大小,现在设块大小指定为b字节,那么一个块可以有8b个bit,这样大小的一个块位图就可以表示8b个块的占用情况,因此一个块组最多可以有8b个块,如果整个分区有s个块,那么就可以有s/(8b)个块组。格式化时可以用-g
参数指定一个块组有多少个块,但是通常不需要手动指定,mke2fs
工具会计算出最优的数值。
inode位图(inode Bitmap)
-
-
inode表占多少个块在格式化时就要决定并写入块组描述符中,
mke2fs
格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode。由于数据块占了整个块组的绝大部分,也可以近似认为数据块有多少个8KB就分配多少个inode,换句话说,如果平均每个文件的大小是8KB,当分区存满的时候inode表会得到比较充分的利用,数据块也不浪费。如果这个分区存的都是很大的文件(比如电影),则数据块用完的时候inode会有一些浪费,如果这个分区存的都是很小的文件(比如源代码),则有可能数据块还没用完inode就已经用完了,数据块可能有很大的浪费。如果用户在格式化时能够对这个分区以后要存储的文件大小做一个预测,也可以用mke2fs
的-i
参数手动指定每多少个字节分配一个inode。
数据块(Data Block)
-
根据不同的文件类型有以下几种情况
-
对于常规文件,文件的数据存储在数据块中。
-
对于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外,
ls -l
命令看到的其它信息都保存在该文件的inode中。注意这个概念:目录也是一种文件,是一种特殊类型的文件。 -
对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
-
设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
-
e2fsck
检查文件系统一致性时,第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。
块位图(Block Bitmap)
从Block 24开始就是数据块了。块组描述符中指出,空闲的数据块有986个,由于文件系统是新创建的,空闲块是连续的Block 38-1023,用掉了前面的Block 24-37。从块位图中可以看出,前37位(前4个字节加最后一个字节的低5位)都是1,就表示Block 1-37已用:
001800 ff ff ff ff 1f 00 00 00 00 00 00 00 00 00 00 00
001810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
001870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80
001880 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*
在块位图中,Block 38-1023对应的位都是0(一直到001870那一行最后一个字节的低7位),接下来的位已经超出了文件系统的空间,不管是0还是1都没有意义。可见,块位图每个字节中的位应该按从低位到高位的顺序来看。以后随着文件系统的使用和添加删除文件,块位图中的1就变得不连续了。
块组描述符指出,空闲的inode有117个,由于文件系统是新创建的,空闲的inode也是连续的,inode编号从1到128,空闲的inode编号从12到128。从inode位图可以看出,前11位都是1,表示前11个inode已用:
001c00 ff 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00
001c10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
*
以后随着文件系统的使用和添加删除文件,inode位图中的1就变得不连续了。
001c00这一行的128位就表示了所有inode,因此下面的行不管是0还是1都没有意义。已用的11个inode中,前10个inode是被ext2文件系统保留的,其中第2个inode是根目录,第11个inode是lost+found
目录,块组描述符也指出该组有两个目录,就是根目录和lost+found
。
下面用debugfs
打开fs
文件,然后在提示符下输入help
看看它都能做哪些事情:
$ debugfs fs
debugfs 1.40.2 (12-Jul-2007)
debugfs: help
在debugfs
的提示符下输入stat /
命令,这时在新的一屏中显示根目录的inode信息:
Inode: 2 Type: directory Mode: 0755 Flags: 0x0 Generation: 0 User: 1000 Group: 1000 Size: 1024 File ACL: 0 Directory ACL: 0 Links: 3 Blockcount: 2 Fragment: Address: 0 Number: 0 Size: 0 ctime: 0x4764cc3b -- Sun Dec 16 14:56:59 2007 atime: 0x4764cc3b -- Sun Dec 16 14:56:59 2007 mtime: 0x4764cc3b -- Sun Dec 16 14:56:59 2007 BLOCKS: (0):24 TOTAL: 1
按q退出这一屏,然后用quit
命令退出debugfs
:
debugfs: quit
把以上信息和od
命令的输出对照起来分析:
我们知道,一个文件除了数据需要存储之外,一些描述信息也需要存储,例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是
ls -l
命令看到的那些信息,这些信息存在inode
中而不是数据块中。每个文件都有一个inode,一个块组中的所有inode组成了inode表。