引言
这一节讲述如何快速的了解一个Linux文件系统的设计方式,文件系统的设计当然没有这篇文章说的那么简单,但是对于我们大致了解Linux整体的设计思路入门还是不错的。
简单的文件系统如何设计
如果我们自己从最简单的角度考虑设计一个基本的文件系统,并且让一个文件系统正常工作,我们可以用一个常规的文件读写举例。 一个最简单的文件系统包含下面的处理流程: 首先文件数据从0开始记录,每一个文件在文件系统中有名称,大小和位置三条基本信息。 如果没有文件系统的辅助,我们需要自行考虑文件的磁盘存储位置,如果从磁盘区域1到区域10根据文件的大小存储到块设备的对应位置,并且需要记录当前块的文件写入开始结束和结束为止,记录存储的数据大小。
模式切换
在Linux 用户态内核态硬件三者的关系,用户态是用户看得见的操作,比如想要读取某一个文件或者想要某一个文件改几个字,用户的这些指令通过内核转为机器码命令,然后内核态的文件系统对于磁盘进行IO操作。 也就是说 用户模式:发送指令操作 -> 内核模式:翻译用户指令为块设备可以识别的命令 -> 硬件:根据
在一个简单的文件系统中,用户模式切换到内核模式进行文件管理只需要关心文件大小,文件位置和文件名称。
Linux文件系统结构
Linux的文件系统是树状结构设计,同时文件系统可以支持不同格式,不同文件系统的差别主要在最大支持操作文件大小,文件系统本身大小以及各种文件操作速度差别上。
文件系统比如ext2、ext3、ext4,它们的文件大小和存储形式和存储的位置都有差别,那么Linux是如何处理的呢?
Linux文件系统对于文件IO操作进行下面的抽象,无论文件系统的结构形式如何变动,最终都是通过和下面提到相关的接口来完成交互的。
吐槽:有点像是设计模式的外观模式
- 创建删除:create/unlink
- 打开关闭:open/close
- 打开文件读数据:read
- 打开文件写数据:write
- 打开文件移动到指定位置:lseek
- 特殊文件系统特殊操作:....
这一点和Linux管理块设备类似,在[[Linux与外部结构介绍]]中提到了块设备对于文件系统提供了通用层块进行抽象,对于用户模式看到的不同单块设备之间是没有差别的,而真正的驱动处理出现在内核态中。
读取文件数据流程
在Linux中读取一个文件的流程如下:
- 各个文件系统通用处理
- 文件系统专用处理,请求系统调用对应处理指令
- 设备驱动执行读写数据操作
- 块设备驱动完成读写指令操作
从逻辑结构来看,整个交互流程很简单,然而实际上这都是Linux的设计者们不断努力的成果。
数据流动
在Linux数据的种类分为元数据和数据,元数据和文件名称,文件大小,文件位置相关,这些参数用于内核态读取块设备作为参考。而数据则是我们日常使用的视频数据,文本数据。
元数据其实出了上面的信息种类之外,还包含下面的内容:
- 种类:判断文件是保存数据的普通文件还是目录还是其他文件,也就是文件类型
- 时间信息:文件的创建时间、最后一次访问时间、最后一次修改时间
- 权限信息:Linux权限控制用户访问。
我们可以通过df
命令和相关参数可以详细的了解文件系统的参数和运行情况,df
是一个重要的运维命令,我们可以通过它了解到磁盘的容量情况。容量的限制是磁盘管理中的核心,对于Linux文件管理系统来说有下面几种磁盘的配额方式:
- 用户配额:用户配额通常指的是/home,通常每一个用户家目录有固定的额度配比
- 子卷配额:限制名字为子卷的单元可用容量。
- 目录配额:可以通过特定目录的可用容量,比如共享目录的用户可用容量,ext4和xfs可以设置目录配额。
在文件系统的配额中除了对于针对不同类型的配额之外,还需要考虑系统正常运行运作的系统配额,也就是说如果给用户和系统按照一刀切的方式配比100%的方式划分磁盘是一件危险的操作,通常对于一块磁盘来说保持不超过80%是比较常见设置。
文件系统意外恢复
对于数据的管理最常见的问题是数据不一致的问题,就是一个写入没有完成时候突然断电,这种情况并不算少见,Linux提供了下面的两种方式解决断电数据状态不一致的问题:
- 日志:通常出现在ext4和xfs的文件系统。
- 写时复制:通常为btrfs的实现。
日志方式: 使用日志的情况要多一点,主要是日志的方式简单易懂,也方便恢复,操作主要分为下面表两个步骤:
- 数据修改之前把原子操作记录到日志当中
- 宕机恢复的时候根据日志记录内容还原文件状态。
如果异常情况发生在日志记录之前,那么可以直接丢弃写入一部分的日志,当作文件状态没有更改过。而如果异常状态发生在原子操作的过程中,则根据日志的记录把操作重新执行一遍即可。
当然现代的文件系统更多的是来自于系统的BUG产生的数据不一致问题,现代的SSD磁盘写入都非常快基本不会出现写入时数据不一致问题。
写时复制方式: 了解写时复制需要了解一些传统的比如ext4和xfs的文件系统工作机制,这些文件系统在文件创建之后就会固定存放到磁盘的某个位置,哪怕是删除或者更新文件内容也只是在原有空间上进行操作。
而btrfs的写时复制的文件系统管理方案则比较特殊,创建之后的文件每次更新都会放到不一样的位置。写时复制就是说更新和写入都是一次类似“复制”的操作,当新数据写入完成再把引用更新即可,原有的内容只要不被新文件覆盖还是可以被找到。
那么如果在写入的时候突然断电怎么办?其实这时候数据是在另一个地方操作的,数据写入到一半也不会对旧数据有影响, 如果是其他操作情况下比如写入刚完成没有更新引用的情况,此时只需要把引用更新一下即可。
其实磁盘是没有删除这个概念的,计算机所谓的删除只是操作系统无法通过常规操作访问被删除的文件而已,但是通过一些特殊处理还是有办法通过文件碎片恢复原始文件的。
无法恢复的意外
如果是文件系统的BUG无法恢复的意外,对于不同的文件系统来说处理方案也不同,除此之外几乎所有的文件系统都有通用的fsck命令进行恢复,这个命令是有可能恢复数据状态的,下面是关于这个命令的介绍:
fsck 命令 Linux fsck(英文全拼:file system check)命令用于检查与修复 Linux 档案系统,可以同时检查一个或多个 Linux 档案系统。
语法 fsck [-sACVRP] [-t fstype] [--] [fsck-options] filesys [...]
参数 :
- filesys : device 名称(eg./dev/sda1),mount 点 (eg. / 或 /usr)
- -t : 给定档案系统的型式,若在 /etc/fstab 中已有定义或 kernel 本身已支援的则不需加上此参数
- -s : 依序一个一个地执行 fsck 的指令来检查
- -A : 对/etc/fstab 中所有列出来的 partition 做检查
- -C : 显示完整的检查进度
- -d : 列印 e2fsck 的 debug 结果
- -p : 同时有 -A 条件时,同时有多个 fsck 的检查一起执行
- -R : 同时有 -A 条件时,省略 / 不检查
- -V : 详细显示模式
- -a : 如果检查有错则自动修复
- -r : 如果检查有错则由使用者回答是否修复
实例
检查 msdos
档案系统的/dev/hda5
是否正常,如果有异常便自动修复 :
fsck -t msdos -a /dev/hda5
然而这个命令看似很强大,但是存在一些很严重的性能问题:
- 遍历文件系统并且检查文件系统的一致性同时还会修复不一致的地方,但是严重的问题如果文件系统非常庞大恢复速度会长达几个小时或者几天。
- 如果中途失败,崩溃的不只是机器
- 修复成功不一定会恢复到期望状态,同时对于一切不一致的数据和元数据都会删除。