文件系统
文件系统就是在磁盘或者其他存储设备上组织文件的方法和数据结构。
文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。
从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。
具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
Linux之ext2/ext3文件系统
Linux最传统的磁盘文件系统(filesystem)使用的是ext2,ext3则是ext2的升级版,但实际上两者的结构差别并不是很大,主要在于ext3加入了日志系统。
不同的文件系统对于文件的组织处理方法是不同的。磁盘分区完毕以后需要进行格式化,可以利用某些格式化工具(如linux下的mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化的过程会在磁盘上写一些管理存储布局的信息。经过格式化的分区,我们就可以按照相应文件系统的规则来读取和存放数据。
ext2/ext3文件系统的文件存储
众所周知,对于一个文件,除了包含其数据内容,还有这许多其他的属相。如linux下的文件就有着其所属者,所属组,以及权限等等其他的属性。那么文件系统是怎么组织一个文件的存储的呢?
对于linux下同一个文件系统(分区)内的每个文件而言,都有着唯一的一个i节点值。可以说文件名是用来帮助人们来区分文件的,但是对于计算机而言,是按照inode的不同来区别文件的。这里可以衍生出一个问题,Linux软硬链接下的软硬链接有何不同,有兴趣的可以搜下。
所以,在磁盘物理存储空间上,我们可以这个认为,有一块区间,inode table,它按照i节点值的存放着每个文件除了文件名以外的文件相关信息。同样的,磁盘上还存在一个区间是Data Block,它则存放着文件的具体数据内容。
对于一个文件而言,它的inode大小是固定的,大小为128bit。存放在inode table之中,并且记录着该文件的相关属性,以及文件内容放置在哪一个 Block
之内。
如下为一个文件的inode中会拥有的信息:
• 该文件的拥有者与群组(owner/group);
• 该文件的权限(read/write/excute);
• 该文件的类型(type);
• 该文件建立或状态改变的时间(ctime)、最近一次的读取时间(atime)、最近修改的时间(mtime);
• 该文件的容量;
• 定义文件特性的旗标(flag),如 SetUID...;
• 该文件真正内容的指向,具体存放文件数据内容的Data Block数据块号;
• 该文件的引用计数(参考硬链接)
所以说我们可以根据一个文件的i节点值找到文件inode信息,然后就可以获取相关文件信息了,同时根据inode中对Data Block中数据块的指向,获取数据的真正内容。
除此之外,还有另外一个问题,正如上面所说的那样,inode中并没有存放文件名称,那么我们是怎么可以通过文件名称来确定相应的i节点值呢?
这就要牵扯到另外一个文件类型了,Linux下一切皆文件,目录也是文件的一种。有着对应的i节点值,和相应的数据呢绒。那么目录中存放着什么数据内容呢?
简而言之,目录中的文件内容则存放着该目录下的文件名和i节点号码。所以我们就可以从最上层目录开始,然后依次找到次级目录,…直到找到对应的文件,取出i节点值,就可以得到相应的文件数据内容了。
至于最上层目录的i节点,就要参考挂载点的概念了,对于一个分区,它一定是挂载在linux目录树中的某个目录上的,这个目录就是挂载点。挂载点就是进入此分区的一个入口,对于这个挂载点而言,它的i节点值是确定的。
比如linux下/目录一般都是一个挂载点,通过
ls -id /
命令查看目录的i节点值,就会发现其值为2。同样的,如果/home/目录也是一个挂载点的话,他的i节点值也是2。
ext2/ext3文件系统的结构
下图是一个磁盘分区格式化成ext2文件系统后的存储布局。
然后我们介绍几个中心概念:
-
block:
对于ext2/ext3文件系统而言,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的,启动块是由PC标准规定的,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。
-
Block Group:
启动块之后才是ext2文件系统的开始,ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。
-
Super Block:
SuperBlock是记录整个filesystem相关信息的地方。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。还有一个valid bit数组,来记录此文件系统是否被挂载。Super Block在每个块组的开头都有一份拷贝。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。
-
GDT,Group Descriptor Table
Group Descriptor Table,块组描述符。整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符(Group Descriptor)存储一个块组的描述信息,块位图块号,inode位图块号,inode表块号,数据块快号,空闲inode计数,空闲块计数,自由块计数等等。如果一旦块组描述符意外损坏就会丢失整个块组的数据。
-
块位图(Block Bitmap)
Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用。本身占用一个块的大小,主要使用其的一个bit位来代表本块组中的一个块,bit为1表示该块已用,这个bit为0表示该块空闲可用。
用df命令统计整个磁盘的已用空间非常快是因为只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。
一个块组中的块是这样利用的:数据块(Data Block)存储所有文件的数据,比如某个分区的块大小是1024字节,某个文件是2049字节,那么就需要三个数据块来存,即使第三个块只存了一个字节用df命令统计整个磁盘的已用空间非常快呢?因为只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。
与此相联系的另一个问题是:在格式化一个分区时究竟会划出多少个块组呢?主要的限制在于块位图本身必须只占一个块。用mke2fs格式化时默认块大小是1024字节,可以用-b参数指定块大小,现在设块大小指定为b字节,那么一个块可以有8b个bit,这样大小的一个块位图就可以表示8b个块的占用情况,因此一个块组最多可以有8b个块,如果整个分区有s个块,那么就可以有s/(8b)个块组。格式化时可以用-g参数指定一个块组有多少个块,但是通常不需要手动指定,mke2fs工具会计算出最优的数值。
-
inode位图(inode Bitmap)
和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。
-
inode表(inode Table)
正如前文所述的那样,一个文件的的数据信息被存储到数据块。除了文件名以外的其他文件信息和真正数据块的指向都是存放在inode中。每个文件都有一个inode,一个块组中的所有inode组成了inode表。inode Table占多少个块在格式化时就要决定并写入块组描述符中,每个inode的大小是固定的128bit,所以格式化的时候就确定了inode的数目大小,而每个文件都会占用一个inode(硬链接除外),所以我们可以创建的文件的数目也是要受到inode数目大小的限制的。
mke2fs格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode。由于数据块占了整个块组的绝大部分,也可以近似认为数据块有多少个8KB就分配多少个inode,换句话说,如果平均每个文件的大小是8KB,当分区存满的时候inode table会得到比较充分的利用,数据块也不浪费。如果这个分区存的都是很大的文件(比如电影),则数据块用完的时候inode会有一些浪费,如果这个分区存的都是很小的文件(比如源代码),则有可能数据块还没用完inode就已经用完了,数据块可能有很大的浪费。如果用户在格式化时能够对这个分区以后要存储的文件大小做一个预测,也可以用mke2fs的-i参数手动指定每多少个字节分配一个inode。
-
数据块 Data Block
对于常规文件,文件的数据存储在数据块中。
对于目录,该目录下的所有文件名和目录名以及i节点值存储在数据块中。
对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
Linux ext2/ext3文件系统中inode的主要结构
前文讲了inode的大小是固定的128bit,除了存放除了文件名之外的所有文件属性,还包括存放文件数据内容的数据块的指向。
下图Linux ext2/ext3文件系统的inode节点结构。对于一个inode而言,其使用15个索引来查找数据块,前12个为直接数据块索引,直接指向存储数据的数据块,接下来分别为一级间接块索引,二级间接块索引,三级间接块索引,如下图:
前12个直接数据块索引,直接记录着对应的数据块位置,一级间接块索引指向一个block,该block则真正记录着真正数据块的索引。二级间接块索引则是先指向一个Block,该Block又会记录着其他Block的索引,再找到的Block中才是真正数据块的索引。
最大分区:
因为内核中block数目值定义的是无符号32位数,所以可能定位的block范围为2^32,也就是4G,如果一个block大小为4KB,所以为4G * 4KB = 16TB,这就是ext2/ext3文件系统中最大的分区值。
最大文件:
对于一个索引而言,一般是数据块的号码,一般占用4个字节。15个索引中,最前面的12个索引直接指向12个数据块,一级间接块索引指向一个数据块,该数据块中,最多可拥有block size / 4个索引指向数据块。二级间接索引,可拥有(block size/4)*(block size/4)个索引。三级间接块类似可以做类似的计算。
所以最终的结果,一个inode的最大文件可以拥有12 + (block size/4) + (block size/4)^2 + (block size/4)^3个数据块。如果block size大小为4K,则为(12 + 2^10 + 2^20 + 2^30) * 4kb 约等于4T。
不过在实际中好像,由于内核中其他参数的限制,一个文件的最大值被限定为2TB。下图是由于block size的不同,从而产生的对应的最大分区和最大文件。
![ext2/ext3文件系统最大分区和最大文件限制](https://img-my.csdn.net/uploads/201302/22/1361541266_1776.jpg)
Linux ext3的日志功能
上面所讲的ext2和ext3结构基本相同,那么到底ext3的文件系统比起ext2有哪些升级呢?
对于ext2文件系统中,inode Table和data Block被称为数据存放区域,而其它的 super Block,inode Bitamp,inode Bitmap则通常被称为metadata。由于我们创建新文件,或者修改文件的时候,一般是先将文件内容写入数据存放区域,然后更新metadata。这是需要一定过程的。所以不可避免的会可能出现数据区域和metadata不一致的情况,比如我们已经将数据内容真正写入了数据存放区域,但是因为某些故障没有更新metadata。所以我们的数据就这么丢失了吗?
对于ext2文件系统而言,如果发生这个问题,它一般会在系统重新开机的时候,根据super Block的某些信息来判断是否强制需要进行数据一致性检查,如果需要的话就要metadata和实际数据存放区域进行一致性对比。这需要搜索整个分区,是一个非常费事的操作。
但是对于ext3文件系统而言,专门规划一个区块,作为记录写入或者修订档案时候的步骤。如果发生上述情况,我们只需要去日志记录区块中查看,然后发现具体哪个文件出现了问题,针对该问题做一致性检查,就可以缩小了很多范围。然后修复文件系统。