Linux磁盘管理之文件系统
文件系统类别
Linux支持的常见文件系统
Linux 系统能够支持的文件系统非常多,除 Linux 默认文件系统 Ext2、Ext3 和 Ext4 之外,还能支持
fat16、fat32、NTFS(需要重新编译内核)等 Windows 文件系统。也就是说,Linux 可以通过挂载的方式使用Windows 文件系统中的数据。Linux 所能够支持的文件系统在 "/usr/src/kemels/当前系统版本/fs"目录中(需要在安装时选择),该目录中的每个子目录都是一个可以识别的文件系统。我们介绍较为常见的 Linux 支持的文件系统,
Ext | Linux 中最早的文件系统,由于在性能和兼容性上具有很多缺陷,现在已经很少使用 |
Ext2 | 是 Ext 文件系统的升级版本,Red Hat Linux 7.2 版本以前的系统默认都是 Ext2 文件系统。于 1993 年发布,支持最大 16TB 的分区和最大 2TB 的文件(1TB=1024GB=1024x1024KB) |
Ext3 | 是 Ext2 文件系统的升级版本,最大的区别就是带日志功能,以便在系统突然停止时提高文件系统的可靠性。支持最大 16TB 的分区和最大 2TB 的文件 |
Ext4 | 是 Ext3 文件系统的升级版。Ext4 在性能、伸缩性和可靠性方面进行了大量改进。Ext4 的变化可以说是翻天覆地的,比如向下兼容 Ext3、最大 1EB 文件系统和 16TB 文件、无限数量子目录、Extents 连续数据块 概念、多块分配、延迟分配、持久预分配、快速 FSCK、日志校验、无日志模式、在线碎片整理、inode 增强、默认启用 barrier 等。它是 CentOS 6.3 的默认文件系统 |
xfs | 被业界称为最先进、最具有可升级性的文件系统技术,由 SGI 公司设计,目前最新的 CentOS 7 版本默认使用的就是此文件系统。 |
swap | swap 是 Linux 中用于交换分区的文件系统(类似于 Windows 中的虚拟内存),当内存不够用时,使用交换分区暂时替代内存。一般大小为内存的 2 倍,但是不要超过 2GB。它是 Linux 的必需分区 |
NFS | NFS 是网络文件系统(Network File System)的缩写,是用来实现不同主机之间文件共享的一种网络服务,本地主机可以通过挂载的方式使用远程共享的资源 |
iso9660 | 光盘的标准文件系统。Linux 要想使用光盘,必须支持 iso9660 文件系统 |
fat | 就是 Windows 下的 fatl6 文件系统,在 Linux 中识别为 fat |
vfat | 就是 Windows 下的 fat32 文件系统,在 Linux 中识别为 vfat。支持最大 32GB 的分区和最大 4GB 的文件 |
NTFS | 就是 Windows 下的 NTFS 文件系统,不过 Linux 默认是不能识别 NTFS 文件系统的,如果需要识别,则需要重新编译内核才能支持。它比 fat32 文件系统更加安全,速度更快,支持最大 2TB 的分区和最大 64GB 的文件 |
ufs | Sun 公司的操作系统 Solaris 和 SunOS 所采用的文件系统 |
proc | Linux 中基于内存的虚拟文件系统,用来管理内存存储目录 /proc |
sysfs | 和 proc —样,也是基于内存的虚拟文件系统,用来管理内存存储目录 /sysfs |
tmpfs | 也是一种基于内存的虚拟文件系统,不过也可以使用 swap 交换分区 |
虚拟文件系统(VFS)
VFS是一个抽象层,其向上提供了统一的文件访问接口,而向下则兼容了各种不同的文件系统。不仅仅是诸如Ext2、Ext4、XFS和Btrfs等常规意义上的文件系统,还包括伪文件系统和设备等等内容。。对于应用程序来说,其访问的接口是完全一致的(例如open、read和write等),并不需要关系底层的文件系统细节。也就是一个应用可以对一个文件进行任何的读写,不用关心文件系统的具体实现。另外,VFS实现了一部分公共的功能,例如页缓存和inode缓存等,从而避免多个文件系统重复实现的问题。
VFS的存在可以让Linux操作系统实现非常复杂的文件系统关联关系。如图2所示,该系统根文件系统是Ext3文件系统,而在其/mnt目录下面又分别挂载了Ext4文件系统和XFS文件系统。最后形成了一个由多个文件系统组成的文件系统树。
VFS存在的意义
-
向上,对应用层提供一个标准的文件操作接口;
-
对下,对文件系统提供一个标准的接口,以便其他操作系统的文件系统可以方便的移植到Linux上;
-
VFS内部则通过一系列高效的管理机制,比如inode cache, dentry cache 以及文件系统的预读等技术,使得底层文件系统不需沉溺到复杂的内核操作,即可获得高性能;
-
此外VFS把一些复杂的操作尽量抽象到VFS内部,使得底层文件系统实现更简单。
VFS为底层文件系统提供了抽象,有两种策略提供这种抽象。
-
提供一个最小的通用模型,使得这个模型支持的功能是所有文件系统的最小交集
-
提供一个尽量大的通用模型,使得这个模型包含所有文件系统功能的合集。
Linux采用第二种策略来实现VFS,因此VFS封装了底层文件系统的所有功能和抽象,VFS负责把应用层的请求转发给特定的文件系统。
在处理文件时,应用空间和内核空间使用的对象是不同的。对应用程序来说,文件描述符用来表示一个文件,这个文件描述符是打开文件时内核分配给这个文件的一个整数,注意,这个文件描述符只在本进程内有效;而对于内核来说,则使用一个inode来表示一个文件,这个inode可能对应着应用层多个进程内的多个文件描述符。
VFS建立了应用程序与具体文件系统的联系,其提供了统一的访问接口实现对具体文件系统的访问(例如Ext2文件系统)。那么两者是怎么关联起来的呢?这里涉及如下几个处理流程:
挂载,也就是具体文件系统(例如Ext2)的挂载 打开文件,我们在访问一个文件之前首先要打开它(open)文件访问,进行文件的读写操作(read或者write)其中第1个流程其实是建立VFS和诸如Ext4文件系统的关联,这样当用户在后面打开某个文件的时候,VFS就知道应该调用那个文件系统的函数实现。而第2个流程则是初始化文件系统必要的数据结构和操作函数(例如read和write等),为后面的具体操作做准备
挂载也是用户态发起的命令,就是我们知道的mount命令,该命令执行的时候需要指定文件系统的类型(本文假设Ext2)和文件系统数据的位置(也就是设备)。通过这些关键信息,VFS就可以完成Ext2文件系统的初始化,并将其关联到当前已经存在的文件系统中,也就是建立其文件系统树。
扩展连接
https://blog.csdn.net/jasonchen_gbd/article/details/51511261
文件系统层次分析
由上而下主要分为用户层、VFS层、文件系统层、缓存层、块设备层、磁盘驱动层、磁盘物理层
用户层 | 最上面用户层就是我们日常使用的各种程序,需要的接口主要是文件的创建、删除、打开、关闭、写、读等。 |
VFS层 | 我们知道Linux分为用户态和内核态,用户态请求硬件资源需要调用System Call通过内核态去实现。用户的这些文件相关操作都有对应的System Call函数接口,接口调用 VFS对应的函数。 |
文件系统层 | 不同的文件系统实现了VFS的这些函数,通过指针注册到VFS里面。所以,用户的操作通过VFS转到各种文件系统。文件系统把文件读写命令转化为对磁盘LBA的操作,起了一个翻译和磁盘管理的作用。 |
缓存层 | 文件系统底下有缓存,Page Cache,加速性能。对磁盘LBA的读写数据缓存到这里。 |
块设备层 | 块设备接口Block Device是用来访问磁盘LBA的层级,读写命令组合之后插入到命令队列,磁盘的驱动从队列读命令执行。Linux设计了电梯算法等对很多LBA的读写进行优化排序,尽量把连续地址放在一起。 |
磁盘驱动层 | 磁盘的驱动程序把对LBA的读写命令转化为各自的协议,比如变成ATA命令,SCSI命令,或者是自己硬件可以识别的自定义命令,发送给磁盘控制器。Host Based SSD甚至在块设备层和磁盘驱动层实现了FTL,变成对Flash芯片的操作。 |
磁盘物理层 | 读写物理数据到磁盘介质。 |
文件系统结构与工作原理(主要以ext4为例)
我们都知道,windows文件系统主要有fat、ntfs等,而linux文件系统则种类多的很,主要有VFS做了一个软件抽象层,向上提供文件操作接口,向下提供标准接口供不同文件系统对接,下面主要就以EXT4文件系统为例,讲解下文件系统结构与工作原理:
上面两个图大体呈现了ext4文件系统的结构,从中也相信能够初步的领悟到文件系统读写的逻辑过程。 下面对上图里边的构成元素做个简单的讲解:
引导块MBR | 为磁盘分区的第一个块,记录文件系统分区的一些信息,,引导加载当前分区的程序和数据被保存在这个块中。一般占用2kB, |
超级块superblock | 超级块用于存储文件系统全局的配置参数(譬如:块大小,总的块数和inode数)和动态信息(譬如:当前空闲块数和inode数),其处于文件系统开始位置的1k处,所占大小为1k。为了系统的健壮性,最初每个块组都有超级块和组描述符表(以下将用GDT)的一个拷贝,但是当文件系统很大时,这样浪费了很多块(尤其是GDT占用的块多),后来采用了一种稀疏的方式来存储这些拷贝,只有块组号是3, 5 ,7的幂的块组(譬如说1,3,5,7,9,25,49…)才备份这个拷贝。通常情况下,只有主拷贝(第0块块组)的超级块信息被文件系统使用,其它拷贝只有在主拷贝被破坏的情况下才使用。 |
块组 | 每个块组包含一个块位图块,一个 inode 位图块,一个或多个块用于描述 inode 表和用于存储文件数据的数据块,除此之外,还有可能包含超级块和所有块组描述符表(取决于块组号和文件系统创建时使用的参数)。下面将对这些元数据作一些简要介绍。 |
块位图 | 块位图用于描述该块组所管理的块的分配状态。如果某个块对应的位未置位,那么代表该块未分配,可以用于存储数据;否则,代表该块已经用于存储数据或者该块不能够使用(譬如该块物理上不存在)。由于块位图仅占一个块,因此这也就决定了块组的大小。 |
Inode位图 | Inode位图用于描述该块组所管理的inode的分配状态。我们知道inode是用于描述文件的元数据,每个inode对应文件系统中唯一的一个号,如果inode位图中相应位置位,那么代表该inode已经分配出去;否则可以使用。由于其仅占用一个块,因此这也限制了一个块组中所能够使用的最大inode数量。 |
Inode表 | Inode表用于存储inode信息。它占用一个或多个块(为了有效的利用空间,多个inode存储在一个块中),其大小取决于文件系统创建时的参数,由于inode位图的限制,决定了其最大所占用的空间。 |
引导区块
早期的 Linux 系统为了相容于 Windows 的磁盘,因此使用的是支持 Windows 的 MBR(Master Boot Record, 主要开机纪录区) 的方式来处理开机管理程序与分区表!而开 机管理程序纪录区与分区表则通通放在磁盘的第一个扇区,这个扇区通常是 512Bytes 的大 小 (旧的磁盘扇区都是 512Bytes 喔!),所以说,第一个扇区 512Bytes会有这两个数据: 主要开机记录区(Master Boot Record, MBR):可以安装开机管理程序的地方,有446 Bytes分区表(partition table):记录整颗硬盘分区的状态,有64 Bytes
当你的操作系统为Windows时,那么第一到第四个分区的代号应该就是C, D, E, F。当你有数据要写入F盘时,你的数据会被写入这颗磁盘的301~400号柱面之间的意思。
根据上面的图示与说明,我们可以得到几个重 点信息:其实所谓的“分区”只是针对那个64 Bytes的分区表进行设置而已!
硬盘默认的分区表仅能写入四组分区信息 这四组分区信息我们称为主要(Primary)或延伸(Extended)分区。
分区的最小单位“通常”为柱面(cylinder) 当系统要写入磁盘时,一定会参考磁盘分区表,才能针对某个分区进行数据的处理
MBR分区表
MBR 中,第一个扇区最重要,里面有主要开机记录(Master boot record, MBR)及分区表(partition
table),其中主要开机记录占 446 bytes,分区表占 64 bytes。
分区表只有 64 bytes,最多只能存储 4 个分区,这 4个分区为主分区(Primary)和扩展分区(Extended)。其中扩展分区只有一个,它使用其它扇区用记录额外的分区表,因此通过扩展分区可以分出更多分区,这些分区称为逻辑分区。
Linux 也把分区当成文件,分区文件的命名方式为:磁盘文件名 + 编号,例如 /dev/sda1。注意,逻辑分区的编号从 5 开始。
GPT 磁盘分区表
在扇区的定义上面, 大多会使用所谓的逻辑区块位址(Logical Block Address, LBA)来处理。GPT 将磁盘所有区块以此LBA(默认为 512Bytes 喔!) 来规划, 而第一个 LBA 称为 LBA0 (从 0 开始编号) GPT 使用了 34 个 LBA区块来纪录分区 信息
- LBA0 (MBR 相容区块) 与 MBR 模式相似的,这个相容区块也分为两个部份,一个就是跟之前 446 Bytes 相似的区块,储存了第一阶段的开机管理程序! 而在原本的分区表的纪录区内,这个相容模式 仅放入一个特殊标志的分区,用来表示此磁盘为 GPT格式之意。而不懂 GPT 分区表的 磁盘管理程序, 就不会认识这颗磁盘,除非用户有特别要求要处理这颗磁盘,否则该管理软件不能修改此分区信息,进一步保护了此磁盘!
- LBA1 (GPT 表头纪录) 这个部份纪录了分区表本身的位置与大小,同时纪录了备份用的 GPT 分区 (就是前面谈 到的在最后 34 个LBA 区块) 放置的位置, 同时放置了分区表的检验机制码 (CRC32),操作系统可以根据这个检验码来判断 GPT是否正确。若有错误,还可以通 过这个纪录区来取得备份的 GPT(磁盘最后的那个备份区块) 来恢复 GPT 的正常运 行!
- LBA2-33 (实际纪录分区信息处) 从 LBA2 区块开始,每个 LBA 都可以纪录 4 笔分区纪录,所以在默认的情况下,总共可 以有
432 = 128 笔分区纪录
GPT 分区已经没有所谓的主、延伸、逻辑 分区的概念,既然每笔纪录都可以独立存在, 当然每个都可以视为是主分区!每一个分区都可以拿来格式化使用
开机流程
CMOS是记录各项硬件参 数且嵌入在主板上面的储存器,
BIOS则是一个写入到主板上的一个固件(再次说明, 固件就 是写入到硬件上的一个软件程序)。这个BIOS就是在开机的时候,计算机系统会主动执行的 第一个程序了
整个开机流程到操作系统之前的动作应该是这样的:
- BIOS:开机主动执行的固件,会认识第一个可开机的设备;
- MBR:第一个可开机设备的第一个扇区内的主要开机记录区块,内含开机管理程序;
- 开机管理程序(boot loader):一支可读取核心文件来执行的软件;
- 核心文件:开始操作系统的功能
如果你的分区表为 GPT 格式的话,那么 BIOS 也能够从 LBA0 的 MBR 相容 区块读取第一阶段的开机管理程序码,
块组描述符
- GDT用于存储块组描述符,其占用一个或者多个数据块,具体取决于文件系统的大小。
- 它主要包含块位图,inode位图和inode表位置,当前空闲块数,inode数以及使用的目录数(用于平衡各个块组目录数)。
- 每个块组都对应这样一个描述符,目前该结构占用32个字节,因此对于块大小为4k的文件系统来说,每个块可以存储128个块组描述符。
- 由于GDT对于定位文件系统的元数据非常重要,因此和超级块一样,也对其进行了备份。
- GDT在每个块组(如果有备份)中内容都是一样的,其所占块数也是相同的。
- 从上面的介绍可以看出块组中的元数据譬如块位图,inode位图,inode表其位置不是固定的,当然默认情况下,文件系统在创建时其位置在每个块组中都是一样的,如图2所示(假设按照稀疏方式存储,且n不是3,5,7的幂
以上这几个构成元素所处的磁盘块成为文件系统的元数据块,剩余的部分则用来存储真正的文件内容,称为数据块,而数据块其实也包含数据和目录。
文件/目录的底层实现
读取文件
读取文件的大体过程如下:
- 根据文件所在目录的inode信息,找到目录文件对应数据块
- 根据文件名从数据块中找到对应的inode节点信息
- 从文件inode节点信息中找到文件内容所在数据块块号
- 读取数据块内容
到这里,相信很多人会有一个疑问,我们知道一个文件只有一个Inode节点来存放它的属性信息,那么你可能会想如果一个大文件,那它的block一定是多个的,且可能不连续的,那么inode怎么来表示呢,下面的图告诉你答案:
也就是说,如果文件内容太大,对应数据块数量过多,inode节点本身提供的存储空间不够,会使用其他的间接数据块来存储数据块位置信息,最多可以有三级寻址结构。
剪切拷贝文件
拷贝文件:创建一个新的inode节点,并且拷贝数据块内容
剪切文件:同个分区里边mv,inode节点不变,只是更新目录文件对应数据块里边的文件名和inode对应关系;跨分区mv,则跟拷贝一个道理,需要创建新的inode,因为inode节点不同分区是不能共享的。
软硬连接
软连接:创建软连接会创建一个新的inode节点,其对应数据块内容存储所链接的文件名信息,这样原文件即便删除了,重新建立一个同名的文件,软连接依然能够生效。
硬链接:创建硬链接,并不会新建inode节点,只是links加1,还有再目录文件对应数据块上增加一条文件名和inode对应关系记录;只有将硬链接和原文件都删除之后,文件才会真正删除,即links为0才真正删除。