III Linux系统编程29文件系统2 ext2文件系统.docx
- 文档编号:10009408
- 上传时间:2023-02-07
- 格式:DOCX
- 页数:22
- 大小:358.72KB
III Linux系统编程29文件系统2 ext2文件系统.docx
《III Linux系统编程29文件系统2 ext2文件系统.docx》由会员分享,可在线阅读,更多相关《III Linux系统编程29文件系统2 ext2文件系统.docx(22页珍藏版)》请在冰豆网上搜索。
IIILinux系统编程29文件系统2ext2文件系统
第 29 章 文件系统
2. ext2文件系统
2.1. 总体存储布局
我们知道,一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(例如某种mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息。
下图是一个磁盘分区格式化成ext2文件系统后的存储布局。
图 29.2. ext2文件系统的总体存储布局
文件系统中存储的最小单位是块(Block),一个块究竟多大是在格式化时确定的,例如mke2fs的-b选项可以设定块大小为1024、2048或4096字节。
而上图中启动块(BootBlock)的大小是确定的,就是1KB,启动块是由PC标准规定的,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。
启动块之后才是ext2文件系统的开始,ext2文件系统将整个分区划成若干个同样大小的块组(BlockGroup),每个块组都由以下部分组成。
超级块(SuperBlock)
描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount的时间等等。
超级块在每个块组的开头都有一份拷贝。
块组描述符表(GDT,GroupDescriptorTable)
由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符。
每个块组描述符(GroupDescriptor)存储一个块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。
和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。
通常内核只用到第0个块组中的拷贝,当执行e2fsck检查文件系统一致性时,第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。
块位图(BlockBitmap)
一个块组中的块是这样利用的:
数据块存储所有文件的数据,比如某个分区的块大小是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位图(inodeBitmap)
和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。
inode表(inodeTable)
我们知道,一个文件除了数据需要存储之外,一些描述信息也需要存储,例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是ls-l命令看到的那些信息,这些信息存在inode中而不是数据块中。
每个文件都有一个inode,一个块组中的所有inode组成了inode表。
inode表占多少个块在格式化时就要决定并写入块组描述符中,mke2fs格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode。
由于数据块占了整个块组的绝大部分,也可以近似认为数据块有多少个8KB就分配多少个inode,换句话说,如果平均每个文件的大小是8KB,当分区存满的时候inode表会得到比较充分的利用,数据块也不浪费。
如果这个分区存的都是很大的文件(比如电影),则数据块用完的时候inode会有一些浪费,如果这个分区存的都是很小的文件(比如源代码),则有可能数据块还没用完inode就已经用完了,数据块可能有很大的浪费。
如果用户在格式化时能够对这个分区以后要存储的文件大小做一个预测,也可以用mke2fs的-i参数手动指定每多少个字节分配一个inode。
数据块(DataBlock)
根据不同的文件类型有以下几种情况
∙对于常规文件,文件的数据存储在数据块中。
∙对于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外,ls-l命令看到的其它信息都保存在该文件的inode中。
注意这个概念:
目录也是一种文件,是一种特殊类型的文件。
∙对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
∙设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
现在做几个小实验来理解这些概念。
例如在home目录下ls-l:
$ls-l
total32
drwxr-xr-x114akaeduakaedu122882008-10-2511:
33akaedu
drwxr-xr-x114ftpftp40962008-10-2510:
30ftp
drwx------2rootroot163842008-07-0405:
58lost+found
为什么各目录的大小都是4096的整数倍?
因为这个分区的块大小是4096,目录的大小总是数据块的整数倍。
为什么有的目录大有的目录小?
因为目录的数据块保存着它下边所有文件和目录的名字,如果一个目录中的文件很多,一个块装不下这么多文件名,就可能分配更多的数据块给这个目录。
再比如:
$ls-l/dev
...
prw-r-----1syslogadm02008-10-2511:
39xconsole
crw-rw-rw-1rootroot1,52008-10-2416:
44zero
xconsole文件的类型是p(表示pipe),是一个FIFO文件,后面会讲到它其实是一块内核缓冲区的标识,不在磁盘上保存数据,因此没有数据块,文件大小是0。
zero文件的类型是c,表示字符设备文件,它代表内核中的一个设备驱动程序,也没有数据块,原本应该写文件大小的地方写了1,5这两个数字,表示主设备号和次设备号,访问该文件时,内核根据设备号找到相应的驱动程序。
再比如:
$touchhello
$ln-s./hellohalo
$ls-l
total0
lrwxrwxrwx1akaeduakaedu72008-10-2515:
04halo->./hello
-rw-r--r--1akaeduakaedu02008-10-2515:
04hello
文件hello是刚创建的,字节数为0,符号链接文件halo指向hello,字节数却是7,为什么呢?
其实7就是“./hello”这7个字符,符号链接文件就保存着这样一个路径名。
再试试硬链接:
$ln./hellohello2
$ls-l
total0
lrwxrwxrwx1akaeduakaedu72008-10-2515:
08halo->./hello
-rw-r--r--2akaeduakaedu02008-10-2515:
04hello
-rw-r--r--2akaeduakaedu02008-10-2515:
04hello2
hello2和hello除了文件名不一样之外,别的属性都一模一样,并且hello的属性发生了变化,第二栏的数字原本是1,现在变成2了。
从根本上说,hello和hello2是同一个文件在文件系统中的两个名字,ls-l第二栏的数字是硬链接数,表示一个文件在文件系统中有几个名字(这些名字可以保存在不同目录的数据块中,或者说可以位于不同的路径下),硬链接数也保存在inode中。
既然是同一个文件,inode当然只有一个,所以用ls-l看它们的属性是一模一样的,因为都是从这个inode里读出来的。
再研究一下目录的硬链接数:
$mkdira
$mkdira/b
$ls-lda
drwxr-xr-x3akaeduakaedu40962008-10-2516:
15a
$ls-laa
total20
drwxr-xr-x3akaeduakaedu40962008-10-2516:
15.
drwxr-xr-x115akaeduakaedu122882008-10-2516:
14..
drwxr-xr-x2akaeduakaedu40962008-10-2516:
15b
$ls-laa/b
total8
drwxr-xr-x2akaeduakaedu40962008-10-2516:
15.
drwxr-xr-x3akaeduakaedu40962008-10-2516:
15..
首先创建目录a,然后在它下面创建子目录a/b。
目录a的硬链接数是3,这3个名字分别是当前目录下的a,a目录下的.和b目录下的..。
目录b的硬链接数是2,这两个名字分别是a目录下的b和b目录下的.。
注意,目录的硬链接只能这种方式创建,用ln命令可以创建目录的符号链接,但不能创建目录的硬链接。
2.2. 实例剖析
如果要格式化一个分区来研究文件系统格式则必须有一个空闲的磁盘分区,为了方便实验,我们把一个文件当作分区来格式化,然后分析这个文件中的数据来印证上面所讲的要点。
首先创建一个1MB的文件并清零:
$ddif=/dev/zeroof=fscount=256bs=4K
我们知道cp命令可以把一个文件拷贝成另一个文件,而dd命令可以把一个文件的一部分拷贝成另一个文件。
这个命令的作用是把/dev/zero文件开头的1M(256×4K)字节拷贝成文件名为fs的文件。
刚才我们看到/dev/zero是一个特殊的设备文件,它没有磁盘数据块,对它进行读操作传给设备号为1,5的驱动程序。
/dev/zero这个文件可以看作是无穷大的,不管从哪里开始读,读出来的都是字节0x00。
因此这个命令拷贝了1M个0x00到fs文件。
if和of参数表示输入文件和输出文件,count和bs参数表示拷贝多少次,每次拷多少字节。
做好之后对文件fs进行格式化,也就是把这个文件的数据块合起来看成一个1MB的磁盘分区,在这个分区上再划分出块组。
$mke2fsfs
mke2fs1.40.2(12-Jul-2007)
fsisnotablockspecialdevice.
Proceedanyway?
(y,n)(输入y回车)
Filesystemlabel=
OStype:
Linux
Blocksize=1024(log=0)
Fragmentsize=1024(log=0)
128inodes,1024blocks
51blocks(4.98%)reservedforthesuperuser
Firstdatablock=1
Maximumfilesystemblocks=1048576
1blockgroup
8192blockspergroup,8192fragmentspergroup
128inodespergroup
Writinginodetables:
done
Writingsuperblocksandfilesystemaccountinginformation:
done
Thisfilesystemwillbeautomaticallycheckedevery27mountsor
180days,whichevercomesfirst.Usetune2fs-cor-itooverride.
格式化一个真正的分区应该指定块设备文件名,例如/dev/sda1,而这个fs是常规文件而不是块设备文件,mke2fs认为用户有可能是误操作了,所以给出提示,要求确认是否真的要格式化,输入y回车完成格式化。
现在fs的大小仍然是1MB,但不再是全0了,其中已经有了块组和描述信息。
用dumpe2fs工具可以查看这个分区的超级块和块组描述符表中的信息:
$dumpe2fsfs
dumpe2fs1.40.2(12-Jul-2007)
Filesystemvolumename:
Lastmountedon:
FilesystemUUID:
8e1f3b7a-4d1f-41dc-8928-526e43b2fd74
Filesystemmagicnumber:
0xEF53
Filesystemrevision#:
1(dynamic)
Filesystemfeatures:
resize_inodedir_indexfiletypesparse_super
Filesystemflags:
signeddirectoryhash
Defaultmountoptions:
(none)
Filesystemstate:
clean
Errorsbehavior:
Continue
FilesystemOStype:
Linux
Inodecount:
128
Blockcount:
1024
Reservedblockcount:
51
Freeblocks:
986
Freeinodes:
117
Firstblock:
1
Blocksize:
1024
Fragmentsize:
1024
ReservedGDTblocks:
3
Blockspergroup:
8192
Fragmentspergroup:
8192
Inodespergroup:
128
Inodeblockspergroup:
16
Filesystemcreated:
SunDec1614:
56:
592007
Lastmounttime:
n/a
Lastwritetime:
SunDec1614:
56:
592007
Mountcount:
0
Maximummountcount:
30
Lastchecked:
SunDec1614:
56:
592007
Checkinterval:
15552000(6months)
Nextcheckafter:
FriJun1314:
56:
592008
Reservedblocksuid:
0(userroot)
Reservedblocksgid:
0(grouproot)
Firstinode:
11
Inodesize:
128
Defaultdirectoryhash:
tea
DirectoryHashSeed:
6d0e58bd-b9db-41ae-92b3-4563a02a5981
Group0:
(Blocks1-1023)
Primarysuperblockat1,Groupdescriptorsat2-2
ReservedGDTblocksat3-5
Blockbitmapat6(+5),Inodebitmapat7(+6)
Inodetableat8-23(+7)
986freeblocks,117freeinodes,2directories
Freeblocks:
38-1023
Freeinodes:
12-128
128inodespergroup,8inodesperblock,so:
16blocksforinodetable
根据上面讲过的知识简单计算一下,块大小是1024字节,1MB的分区共有1024个块,第0个块是启动块,启动块之后才算ext2文件系统的开始,因此Group0占据第1个到第1023个块,共1023个块。
块位图占一个块,共有1024×8=8192个bit,足够表示这1023个块了,因此只要一个块组就够了。
默认是每8KB分配一个inode,因此1MB的分区对应128个inode,这些数据都和dumpe2fs的输出吻合。
用常规文件制作而成的文件系统也可以像磁盘分区一样mount到某个目录,例如:
$sudomount-oloopfs/mnt
$cd/mnt/
$ls-la
total17
drwxr-xr-x3akaeduakaedu10242008-10-2512:
20.
drwxr-xr-x21rootroot40962008-08-1808:
54..
drwx------2rootroot122882008-10-2512:
20lost+found
-oloop选项告诉mount这是一个常规文件而不是一个块设备文件。
mount会把它的数据块中的数据当作分区格式来解释。
文件系统格式化之后在根目录下自动生成三个子目录:
.,..和lost+found。
其它子目录下的.表示当前目录,..表示上一级目录,而根目录的.和..都表示根目录本身。
lost+found目录由e2fsck工具使用,如果在检查磁盘时发现错误,就把有错误的块挂在这个目录下,因为这些块不知道是谁的,找不到主,就放在这里“失物招领”了。
现在可以在/mnt目录下添加删除文件,这些操作会自动保存到文件fs中。
然后把这个分区umount下来,以确保所有的改动都保存到文件中了。
$sudoumount/mnt
注意,下面的实验步骤是对新创建的文件系统做的,如果你在文件系统中添加删除过文件,跟着做下面的步骤时结果可能和我写的不太一样,不过也不影响理解。
现在我们用二进制查看工具查看这个文件系统的所有字节,并且同dumpe2fs工具的输出信息相比较,就可以很好地理解文件系统的存储布局了。
$od-tx1-Axfs
00000000000000000000000000000000000000
*
000400800000000004000033000000da030000
00041075000000010000000000000000000000
...
其中以*开头的行表示这一段数据全是零因此省略了。
下面详细分析od输出的信息。
从000000开始的1KB是启动块,由于这不是一个真正的磁盘分区,启动块的内容全部为零。
从000400到0007ff的1KB是超级块,对照着dumpe2fs的输出信息,详细分析如下:
图 29.3. 超级块
超级块中从0004d0到末尾的204个字节是填充字节,保留未用,上图未画出。
注意,ext2文件系统中各字段都是按小端存储的,如果把字节在文件中的位置看作地址,那么靠近文件开头的是低地址,存低字节。
各字段的位置、长度和含义详见[ULK]。
从000800开始是块组描述符表,这个文件系统较小,只有一个块组描述符,对照着dumpe2fs的输出信息分析如下:
...
Group0:
(Blocks1-1023)
Primarysuperblockat1,Groupdescriptorsat2-2
ReservedGDTblocksat3-5
Blockbitmapat6(+5),Inodebitmapat7(+6)
Inodetableat8-23(+7)
986freeblocks,117freeinodes,2directories
Freeblocks:
38-1023
Freeinodes:
12-128
...
图 29.4. 块组描述符
整个文件系统是1MB,每个块是1KB,应该有1024个块,除去启动块还有1023个块,分别编号为1-1023,它们全都属于Group0。
其中,Block1是超级块,接下来的块组描述符指出,块位图是Block6,因此中间的Block2-5是块组描述符表,其中Block3-5保留未用。
块组描述符还指出,inode位图是Block7,inode表是从Block8开始的,那么inode表到哪个块结束呢?
由于超级块中指出每个块组有128个inode,每个inode的大小是128字节,因此共占16个块,inode表的范围是Block8-23。
从Block24开始就是数据块了。
块组描述符中指出,空闲的数据块有986个,由于文件系统是新创建的,空闲块是连续的Block38-1023,用掉了前面的Block24-37。
从块位图中可以看出,前37位(前4个字节加最后一个字节的低5位)都是1,就表示Block1-37已用:
001800ffffffff1f0000000000000000000000
00181000000000000000000000000000000000
*
00187000000000000000000000000000000080
001880ffffffffffffffffffffffffffffffff
*
在块位图中,Block38-1023对应的位都是0(一直到001870那一行最后一个字节的低7位),接下来的位已经超出了文件系统的空间,不管是0还是1都没有意义。
可见,块位图每个字节中的位应该按从低位到高位的顺序来看。
以后随着文件系统的使用和添加删除文件,块位图中的1就变得不连续了。
块组描述符指出,空闲的inode有117个,由于文件系统是新创建的,空闲的inode也是连续的,inode编号从1到128,空闲的inode编号从12到128。
从inode位图可以看出,前11位都是1,表示前11个inode已用:
001c00ff070000000000000000000000000000
001c10ffffffffffffffffffffffffffffffff
*
以后随着文件系统的使用和添加删除文件,inode位图中的1就变得不连续了。
001c
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- III Linux系统编程29文件系统2 ext2文件系统 Linux 系统 编程 29 文件系统 ext2