Linux文件系统

  不同于windows系统默认的文件系统NTFS或者FAT32,Linux 的正统文件系统则为 EXT (Linux extended file system),包括EXT2EXT3EXT4。在默认的情况下,windows 操作系统并不会识别 Linux 的 Ext 系统。

  不论是什么操作系统,我们的数据储存与读取的重点在于磁盘,而磁盘盘上的物理组成则为(假设此磁盘为单盘片):

  • 扇区(Sector)为最小的物理储存单位,每个扇区为 512 bytes;
  • 将扇区组成一个圆,那就是磁柱(Cylinder),磁柱是磁盘分区(partition)的最小单位;
  • 第一个扇区最重要,里面有:(1)主要启动区(Master boot record, MBR)及分区表(partition table), 其中 MBR 占有 446 bytes,而 partition table 则占有 64 bytes。

  各种接口的磁盘在Linux中的文件名分别为:

  • /dev/sd[a-p] [1-15]:为SCSI, SATA, U盘, Flash闪盘等接口的磁盘文件名;
  • /dev/hd[a-d] [1-63]:为 IDE 接口的磁盘文件名;

硬盘分区Partition和MBR

  对于主机而言,由于可能同时存在多种文件管理场景,需要为不同的使用目的设置不同的文件管理系统。体现在硬件上,便是为不同的文件管理系统分配不同的硬件设备,显然为不同的文件管理系统配备专属的独立存储设备是一件奢侈的事。好在一块硬盘其实可以根据不同的使用场景划分出多个分区,每个分区可以采用不同设置的FileSystem进行格式化,这样可以在充分满足自身需求的前提下,减少对硬件的消耗。

  硬盘划分出Partition只需要记录每个分区Partition的起始和结束磁柱即可,这样,操作系统才知道如果对应分区A,需要到磁柱a和磁柱b之间去存取数据。这些分区的起始和结束磁柱号数据全部记录在MBR(Master Boot Recorder主要开机扇区),MBR就是一块硬盘的最外侧的第0号磁道上,除了记录硬盘的分区信息,还是开机的时候开机管理程序写入的地方。(也是因为外侧磁道的信息密度高,磁道转动相同角度比内侧磁道获取的信息更多,读写效率更快)

  MBR最大的限制在于他要记录开机管理程序的信息,剩余的大小不够存储较多的分区信息,MBR最大只支持4个Partition的记忆,这就是所谓的基本和扩展的Partition最多只能有4个的原因。所以如果预计分区超过4个Partition的话,需要使用3P+1E,并且将3P之后所有的剩余空间分配给Extended分区(Extended分区最多只能有1个),否则3P+1E之后剩下的空间将成为废物而被浪费掉。所以如果要硬盘分区并且预计将使用掉MBR提供的4个Partition(3P+E或4P)那么磁盘的全部容量需要被使用掉,否则剩下的容量也不能再被使用。

  传统的磁盘与文件系统之应用中,一个Partition只能够被格式化成为一个FileSystem,所以我们可以说一个 FileSystem就是一个 Partition。但是由于新技术的利用,可以将一个Partition格式化为多个FileSystem(例如LVM),也能够将多个Partition合成一个FileSystem(LVM, RAID)! 所以目前格式化时已经不再说成针对Partition来格式化了, 通常可以称呼一个可被挂载的数据为一个FileSystem而不是一个Partition喔!

EXT文件系统

  那么EXT文件系统是如何运行的呢?这与操作系统的文件数据有关。文件系统通常会将两部分的数据分别存放在不同的区块:权限与属性放置到inode中,至于实际数据则放置到 block区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inodeblock的总量、使用量、剩余量等。

  每个 inodeblock 都有编号,至于这三个数据的意义可以简略说明如下:

  • superblock:记录此 filesystem 的整体信息,包括 inode/block 的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
  • inode:记录文件的属性,一个文件占用一个 inode,同时记录此文件的数据所在的 block 号码;
  • block:实际记录文件的内容,若文件太大时,会占用多个 block 。

  如图所示,假设某一个文件的属性与权限数据是放置到inode4 号(下图较小方格内),而这个 inode记录了文件数据的实际放置点为 2, 7, 13, 15 这四个block号码,此时我们的操作系统就能够据此来排列磁盘的阅读顺序,可以一口气将四个block内容读出来! 那么数据的读取就如同下图中的箭头所指定的模样了。

inode/block 数据存取示意图

   这种数据存取的方法我们称为索引式文件系统(indexed allocation)。与之相对应的是Windows系统下的FAT格式。FAT 这种格式的文件系统并没有inode存在,所以FAT没有办法将这个文件的所有block在一开始就读取出来。每个block号码都记录在前一个block当中, 他的读取方式有点像底下这样:

FAT文件系统数据存取示意图

  这种方法如果同一个文件数据写入的block分散的太过厉害时,则磁盘读取头将无法在磁盘转一圈就读到所有的数据, 因此磁盘就会多转好几圈才能完整的读取到这个文件的内容,此时文件读取的效能将会变的很差所致。 这个时候可以透过碎片整理将同一个文件所属的blocks汇整在一起,这样数据的读取会比较容易,这也就是Windows系统磁盘需要碎片整理的原因。而由于 Ext 是索引式文件系统,基本上不太需要进行碎片整理的。
  **文件系统一开始就将 inode 与 block 规划好了,除非重新格式化(或者利用 resize2fs 等命令变更文件系统大小),否则 inode 与 block 固定后就不再变动。**为了避免系统上所有的 inode 和 block 都堆积在一起不方便管理,Ext2文件系统在格式化的时候基本上是区分为多个区块群组 (block group) 的,每个区块群组都有独立的 inode/block/superblock 系统。整体来说,Ext2 格式化后有点像底下这样:

img

  在整体的规划当中,文件系统最前面有一个启动扇区(boot sector),这个启动扇区可以安装启动管理程序, 这是个非常重要的设计,因为如此一来我们就能够将不同的启动管理程序安装到个别的文件系统最前端,而不用覆盖整颗硬盘唯一的 MBR。

Data Block (数据区块)

  Data block 是用来放置文件内容数据地方,在 Ext2 文件系统中所支持的 block 大小有 1K, 2K 及 4K 三种而已。在格式化时 block 的大小就固定了,且每个 block 都有编号,以方便 inode 的记录。 不过要注意的是,由于 block 大小的差异,会导致该文件系统能够支持的最大磁盘容量与最大单一文件容量并不相同。 因为 block 大小而产生的 Ext2 文件系统限制如下:

Block 大小1KB2KB4KB
最大单一文件限制16GB256GB2TB
最大文件系统总容量2TB8TB16TB

  需要注意的是,Data block 存在如下限制:

  • 原则上,block 的大小与数量在格式化完就不能够再改变了(除非重新格式化);
  • 每个 block 内最多只能够放置一个文件的数据;
  • 如果文件大于 block size,则一个文件会占用多个 block 数量;
  • 如果文件小于 block size,则该 block 的剩余容量就不能够再被使用了(磁盘空间会浪费)。

  所以block size的大小选择需要根据文件系统的使用情况,太小了存很多大文件会影响检索速度,太大了存很多小文件会造成空间浪费。

inode table(inode 表格)

  inode记录的文件数据至少有以下内容:

  • 该文件的存取模式(read/write/excute);
  • 该文件的拥有者与群组(owner/group);
  • 该文件的容量;
  • 该文件创建或状态改变的时间(ctime);
  • 最近一次的读取时间(atime);
  • 最近修改的时间(mtime);
  • 定义文件特性的旗标(flag),如 SetUID…;
  • 该文件真正内容的指向 (pointer);

  inode 的数量与大小也是在格式化时就已经固定了:

  • 每个 inode 大小均固定为 128 bytes;
  • 每个文件都仅会占用一个 inode 而已;
  • 文件系统能够创建的文件数量与 inode 的数量有关;
  • 系统读取文件时需要先找到 inode,并分析 inode 所记录的权限与用户是否符合,若符合才能够开始实际读取 block 的内容

  inode 要记录的数据非常多,但只有 128bytes , 而 inode 记录一个 block 号码要花掉 4bytes ,假设文件有 400MB 且每个 block 为 4K 时, 至少也要十万笔 block 号码的记录呢!inode 哪有这么多可记录的信息?为此我们的系统很聪明的将 inode 记录 block 号码的区域定义为12个直接,1个间接, 1个双间接与1个三间接记录区。如下图所示:

img

  • 12 个直接指向: $ 12*1K=12K$
    由于是直接指向,所以总共可记录 12 笔记录,总额大小为12K;
  • 间接:$ 256*1K=256K $
    每个block 号码的记录会花去 4bytes,因此 1K 的大小能够记录 256 笔记录;
  • 双间接: $ 256 * 256 * 1K =256^2K $
    第一层 block 会指定 256 个第二层,每个第二层可以指定 256 个号码。
  • 三间接: $ 256256256*1K=256^3K $
    第一层 block 会指定 256 个第二层,每个第二层可以指定 256 个第三层,每个第三层可以指定 256 个号码。

  将直接、间接、双间接、三间接加总,得到 $ 12 + 256 + 256^2 + 256^3 (K) = 16GB $,这就是为什么上述文件系统限制表1K单个最大文件为16G的原因。

Superblock (超级区块)

  Superblock 是记录整个 filesystem 相关信息的地方, 没有 Superblock ,就没有这个 filesystem 了。他记录的信息主要有:

  • block 与 inode 的总量;
  • 未使用与已使用的 inode / block 数量;
  • block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128 bytes);
  • filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
  • 一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。

  Superblock 是非常重要的,因为我们这个文件系统的基本信息都写在这里,因此,如果 superblock 死掉了, 你的文件系统可能就需要花费很多时间去挽救啦!一般来说, superblock 的大小为 1024bytes。相关的 superblock 信息可以用 dumpe2fs 命令来呼叫出来观察喔!

  此外,每个 block group 都可能含有 superblock。虽然文件系统应该仅有一个 superblock,但事实上除了第一个 block group 内会含有 superblock 之外,后续的 block group 若存在 superblock 则该 superblock 主要是做为第一个 block group 内 superblock 的备份,这样可以进行 superblock 的救援。

Filesystem Description (文件系统描述说明)

  这个区段可以描述每个 block group 的开始与结束的 block 号码,以及说明每个区段 (superblock, bitmap, inodemap, data block) 分别介于哪一个 block 号码之间。这部份也能够用 dumpe2fs 来观察的。

Block Bitmap (区块对照表)

  从 block bitmap 当中可以知道哪些 block 是空的,因此我们的系统就能够很快速的找到可使用的空间来处置文件。如果删除某些文件时,那么那些文件原本占用的 block 号码就得要释放出来。

inode Bitmap (inode 对照表)

  和 Block Bitmap 类似,用来记录使用与为使用的 inode 号码。

查询文件系统 dumpe2fs
dumpe2fs [-bh] 装置文件名
选项与参数:
-b :列出保留为坏轨的部分
-h :仅列出 superblock 的数据,不会列出其他的区段内容!

查询文件系统具体信息

目录树

目录

  当我们在 Linux 下的 ext2 文件系统创建一个目录时, ext2 会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并可记录分配到的那块 block 号码; 而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 号码数据。

目录占用的 block 记录的数据示意图

目录树读取

  因为文件名是记录在目录的 block 当中, 因此当我们要读取某个文件时,就务必会经过目录的 inode 与 block ,然后才能够找到那个待读取文件的 inode 号码, 最终才会读到正确的文件的 block 内的数据。

  由于目录树是由根目录开始读起,因此系统透过挂载的信息可以找到挂载点的 inode 号码(通常一个 filesystem 的最顶层 inode 号码会由 2 号开始),此时就能够得到根目录的 inode 内容,并依据该 inode 读取根目录的 block 内的文件名数据,再一层一层的往下读到正确的档名。

  假设读取/etc/passwd文件,系统的读取步骤为:

image-20220421173136914

  1. / 的 inode:
    透过挂载点的信息找到 /dev/hdc2 的 inode 号码为 2 的根目录 inode,且 inode 规范的权限让我们可以读取该 block 的内容(有 r 与 x) ;
  2. / 的 block:
    经过上个步骤取得 block 的号码,并找到该内容有 etc/ 目录的 inode 号码 (2621441);
  3. etc/ 的 inode:
    读取 2621441 号 inode 得知 vbird 具有 r 与 x 的权限,因此可以读取 etc/ 的 block 内容;
  4. etc/ 的 block:
    经过上个步骤取得 block 号码,并找到该内容有 passwd 文件的 inode 号码 (2626155);
  5. passwd 的 inode:
    读取 2626155 号 inode 得知 vbird 具有 r 的权限,因此可以读取 passwd 的 block 内容;
  6. passwd 的 block:
    最后将该 block 内容的数据读出来。

EXT2/EXT3 文件的存取与日志式文件系统的功能

  假设我们想要新增一个文件,此时文件系统的行为是:

  1. 先确定用户对于欲新增文件的目录是否具有 w 与 x 的权限,若有的话才能新增;
  2. 根据 inode bitmap 找到没有使用的 inode 号码,并将新文件的权限/属性写入;
  3. 根据 block bitmap 找到没有使用中的 block 号码,并将实际的数据写入 block 中,且升级 inode 的 block 指向数据;
  4. 将刚刚写入的 inode 与 block 数据同步升级 inode bitmap 与 block bitmap,并升级 superblock 的内容。

  一般来说,我们将 inode table 与 data block 称为数据存放区域,至于其他例如 superblock、 block bitmap 与 inode bitmap 等区段就被称为 metadata (中介数据) ,因为这些部分数据是经常变动的,每次新增、移除、编辑时都可能会影响到这三个部分的数据。

数据的不一致 (Inconsistent) 状态

  在一般正常的情况下,上述的新增动作当然可以顺利的完成。但是如果有个万一怎么办?写入的数据仅有 inode table 及 data block 而已, 最后一个同步升级中介数据的步骤并没有做完,此时就会发生 metadata 的内容与实际数据存放区产生不一致 (Inconsistent) 的情况。

日志式文件系统 (Journaling filesystem)

  为了避免上述提到的文件系统不一致的情况发生,因此我们的前辈们想到一个方式, 如果在我们的 filesystem 当中规划出一个区块,该区块专门在记录写入或修订文件时的步骤, 那不就可以简化一致性检查的步骤了?也就是说:

  1. 预备:当系统要写入一个文件时,会先在日志记录区块中纪录某个文件准备要写入的信息;
  2. 实际写入:开始写入文件的权限与数据;开始升级 metadata 的数据;
  3. 结束:完成数据与 metadata 的升级后,在日志记录区块当中完成该文件的纪录。

  在这样的程序当中,万一数据的纪录过程当中发生了问题,那么我们的系统只要去检查日志记录区块, 就可以知道哪个文件发生了问题,针对该问题来做一致性的检查即可,而不必针对整块 filesystem 去检查, 这样就可以达到快速修复 filesystem 的能力了!这就是日志式文件最基础的功能。

Linux 文件系统的运行

  为了解决Linux编辑写入的效率的问题,因此我们的 Linux 使用的方式是透过一个称为异步处理 (asynchronously) 的方式。所谓的异步处理是这样的:

  当系统加载一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被配置为干净(clean)的。 但如果内存中的文件数据被更改过了(例如你用 nano 去编辑过这个文件),此时该内存中的数据会被配置为脏的 (Dirty)。此时所有的动作都还在内存中运行,并没有写入到磁盘中! 系统会不定时的将内存中配置为『Dirty』的数据写回磁盘,以保持磁盘与内存数据的一致性。 你也可以利用sync命令来手动强迫写入磁盘。

  我们知道内存的速度要比硬盘快的多,因此如果能够将常用的文件放置到内存当中,这不就会添加系统性能吗? 没错!是有这样的想法!因此我们 Linux 系统上面文件系统与内存有非常大的关系喔:

  • 系统会将常用的文件数据放置到主存储器的缓冲区,以加速文件系统的读/写;
  • 承上,因此 Linux 的物理内存最后都会被用光!这是正常的情况!可加速系统效能;
  • 你可以手动使用 sync 来强迫内存中配置为 Dirty 的文件回写到磁盘中;
  • 若正常关机时,关机命令会主动呼叫 sync 来将内存的数据回写入磁盘内;
  • 但若不正常关机(如跳电、死机或其他不明原因),由于数据尚未回写到磁盘内, 因此重新启动后可能会花很多时间在进行磁盘检验,甚至可能导致文件系统的损毁(非磁盘损毁)。

mount point(挂载点)

  前面讲过,Linux 系统中“一切皆文件”,所有文件都放置在以根目录为树根的树形目录结构中。在 Linux 看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构)。

  因此产生的问题是,当在 Linux 系统中使用这些硬件设备时,只有将Linux本身的文件目录与硬件设备的文件目录合二为一,硬件设备才能为我们所用。合二为一的过程称为“挂载”

如果不挂载,通过Linux系统中的图形界面系统可以查看找到硬件设备,但命令行方式无法找到。

  挂载,指的就是将设备文件中的顶级目录连接到 Linux 根目录下的某一目录(最好是空目录),访问此目录就等同于访问设备文件

  每个 filesystem 都有独立的 inode / block / superblock 等信息,这个文件系统要能够链接到目录树才能被我们使用。

磁盘与目录的容量

查询文件系统的整体磁盘使用量 (Disk free):df

df [-ahikHTm] [目录或文件名]
选项与参数:
-a  :列出所有的文件系统,包括系统特有的 /proc 等文件系统;
-k  :以 KBytes 的容量显示各文件系统;
-m  :以 MBytes 的容量显示各文件系统;
-h  :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;
-H  :以 M=1000K 取代 M=1024K 的进位方式;
-T  :连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;
-i  :不用硬盘容量,而以 inode 的数量来显示

当前挂载设备情况

  有趣的是,我们可以使用这个命令查询某个文件或文件夹所属的 partition,并显示该 partition 的容量情况:

image-20220422104658740

  由于 df 主要读取的数据几乎都是针对一整个文件系统,因此读取的范围主要是在 Superblock 内的信息,所以这个命令显示结果的速度非常的快速!

评估文件系统的磁盘使用量(Disk usage):du

du [-ahskm] 文件或目录名称
选项与参数:
-a  :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。
-h  :以人们较易读的容量格式 (G/M) 显示;
-s  :列出总量而已,而不列出每个各别的目录占用容量;
-S  :不包括子目录下的总计,与 -s 有点差别。
-k  :以 KBytes 列出容量显示;
-m  :以 MBytes 列出容量显示;

实体链接与符号链接

img

Hard Link (实体链接, 硬式连结或实际连结)

  hard link 只是在某个目录下新增一个文件名链接到某 inode 号码的关连记录而已。多个文件名可以链接到同一个inode,这样两个文件名的所有相关信息都会一模一样(除了文件名之外)。而如果你将任何一个文件名删除,其实 inode 与 block 都还是存在的。

image-20220422115030084

  hard link 存在以下两个限制:

  • 不能跨 Filesystem;
  • 不能 link 目录。

  不能跨文件系统因为不同系统的inode编号不一样,这很容易理解;而不能 hard link 到目录是因为如果使用 hard link 链接到目录, 链接的数据需要连同被链接目录底下的所有数据都创建链接,会造成环境相当大的复杂度。

Symbolic Link (符号链接,亦即是快捷方式)

  相对于 hard link , Symbolic link 可就好理解多了。 Symbolic link 是在创建一个独立的文件,而这个文件会让数据的读取指向他 link 的那个文件的文件名!由于只是利用文件来做为指向的动作, 所以,当来源文件被删除之后,symbolic link 的文件就无法打开了,这与 hard link 是完全不同的:

Hard link and Symbolic link

  我们尝试建立一个python的软链接,可以看到,两个文件均为python的软链接,指向不同的 inode 号码,当然作为两个独立的文件存在。

image-20220422115459527

  Symbolic Link 与 Windows 的快捷方式可以划上等号,由 Symbolic link 所创建的文件为一个独立的新的文件,所以会占用掉 inode 与 block 。

制作链接:ln

  制作链接用ln指令,这里需要注意的是:使用 ln 如果不加任何参数的话,那么就是 Hard Link

  而修改 Linux 下的 symbolic link 文件时,则更动的其实是『原始档』, 所以不论你的这个原始档被连结到哪里去,只要你修改了连结档,原始档就跟着会被修改。

ln [-sf] 来源文件 目标文件
选项与参数:
-s  :如果不加任何参数就进行连结,那就是hard link,至于 -s 就是symbolic link
-f  :如果 目标文件 存在时,就主动的将目标文件直接移除后再创建!
GitHub 加速计划 / li / linux-dash
6
1
下载
A beautiful web dashboard for Linux
最近提交(Master分支:3 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐