Linux文件系统
前言:什么是文件系统?
文件系统是操作系统用于明确存储设备(常见的是磁盘,也有基于 NAND Flash 的固态硬盘)或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件机构称为文件管理系统,简称文件系统。文件系统由三部分组成:文件系统的接口,对对象操纵和管理的软件集合,对象及属性。从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,当用户不再使用时撤销文件等。
文件系统的基本数据单位是文件,它的目的是对磁盘上的文件进行组织管理,那组织的方式不同,就会形成不同的文件系统。Linux 最经典的一句话是:「一切皆文件」,不仅普通的文件和目录,就连块设备、管道、socket 等,也都是统一交给文件系统管理的。Linux 文件系统会为每个文件分配两个数据结构:索引节点(index node)和目录项(directory entry),它们主要用来记录文件的元信息和目录层次结构。
-
索引节点(inode),用来记录文件的元信息,比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等等。索引节点是文件的唯一标识,它们之间一一对应,也同样都会被存储在硬盘中,所以索引节点同样占用磁盘空间。 -
目录项(dentry),用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来,就会形成目录结构,但它与索引节点不同的是,目录项是由内核维护的一个数据结构,不存放于磁盘,而是缓存在内存。
由于索引节点唯一标识一个文件,而目录项记录着文件的名,所以目录项和索引节点的关系是多对一,也就是说,一个文件可以有多个别名。比如,硬链接的实现就是多个目录项中的索引节点指向同一个文件。注意,目录也是文件,也是用索引节点唯一标识,和普通文件不同的是,普通文件在磁盘里面保存的是文件数据,而目录文件在磁盘里面保存子目录或文件。
目录项和目录
-
虽然名字很相近,但是它们不是一个东西, 目录是个文件,持久化存储在磁盘,而目录项是内核一个数据结构,缓存在内存。 -
如果查询目录频繁从磁盘读,效率会很低,所以内核会把已经读过的目录用目录项这个数据结构缓存在内存,下次再次读到相同的目录时,只需从内存读就可以,大大提高了文件系统的效率。 -
注意,目录项这个数据结构不只是表示目录,也是可以表示文件的。
磁盘读写的最小单位是扇区,扇区的大小只有 512B,很明显,如果每次读写都以这么小为单位,那这读写的效率会非常低。
所以,文件系统把多个扇区组成了一个逻辑块,每次读写的最小单位就是逻辑块(数据块),Linux 中的逻辑块大小为 4KB,也就是一次性读写 8 个扇区,这将大大提高磁盘的读写效率。
索引节点、目录项以及文件数据的关系:

索引节点是存储在硬盘上的数据,为了加速文件的访问,通常会把索引节点加载到内存中。 另外,磁盘进行格式化的时候,会被分成三个存储区域,分别是超级块、索引节点区和数据块区。
-
超级块,用来存储文件系统的详细信息,比如块个数、块大小、空闲块等等。 -
索引节点区,用来存储索引节点。 -
数据块区,用来存储文件或目录数据。
一、Linux 下文件系统的层次结构
在 Linux 系统下我们首先执行以下命令并查看输出:
ls -l /
对于 Linux 软件开发人员肯定已经非常熟悉 Linux 系统的目录结构。文件系统可以根据它们的结构而变化,但在大多数情况下,它们应该符合文件系统层次标准。执行 ls -l /命令查看根目录下列出的目录,你的目录可能与我的目录有些许的不同,但目录应该大致如下所示:

各个目录功能介绍:
-
/ -根目录,整个文件系统层次结构的根目录,所有内容都位于此目录下。 -
/bin -存放基本的可执行的程序(二进制文件),包括最基本的命令,如 ls 和 cp。 -
/boot -包含内核引导加载程序文件。 -
/cdrom -光盘安装点 -
/dev -存放设备文件。 -
/etc -核心系统配置目录,应该只保存配置文件。 -
/home -用户的主目录,保存你的文档,文件,设置等。 -
/lib、/lib32、/lib64、/libx32 -主要目的是存放特定的库,这些库是在/bin 和/sbin 目录里的工具所需要的库,/lib 中的库可以是 32 位或 64 位 -
/lost+found -这个目录一般情况下是空的,当系统非法关机后,如果你丢失了一些文件,在这里能找回来,通常很少用到此目录 -
/media -用作可移动媒体的连接点,如 USB 驱动器。 -
/mnt -临时挂载的文件系统。 -
/opt -可选应用软件包。 -
/proc -当前运行进程的信息。 -
/root -root 用户的主目录。 -
/run -自上次引导以来运行系统的信息。 -
/sbin -包含基本的系统二进制文件,通常只能由 root 用户运行。 -
/srv -系统提供的特定于站点的数据。 -
/tmp -临时文件的存储 -
/usr -通常它不包含主文件夹意义上的用户文件。这意味着用户安装的软件和实用程序,但这并不是说你不能在那里添加个人目录。在这个目录中有/usr/bin、/usr/local 等子目录。 -
/var -变量目录,用于系统日志记录、用户跟踪、缓存等。
二、 文件系统类型
文件系统分很多种类,有许多不同的文件系统实现可用。有些比其他的更快,有些支持更大容量的存储,而另一些只能在更小容量的存储上工作。不同的文件系统有不同的组织数据的方式。由于有很多不同的实现,应用程序需要一种方法来处理不同的操作。因此,有一种东西叫做虚拟文件系统抽象层。它是应用程序和不同文件系统类型之间的一层,因此无论拥有什么文件系统,你的应用程序都能够使用它。
你的磁盘上可以有许多文件系统,这取决于它们是如何分区的
常见的文件系统类型:
-
ext4:这是本地 Linux 文件系统的最新版本。它与旧的 ext2 和 ext3 版本兼容。它支持高达 10 亿字节的磁盘卷和高达 16TB 甚至更多的文件大小。它是 Linux 文件系统的标准选择。 -
Btrfs:“Better or Butter FS”,它是一种新的 Linux 文件系统,提供快照、增量备份、性能提升等功能。它是广泛可用的,但还不太稳定和兼容。 -
XFS:高性能日志文件系统,非常适合具有大文件的系统,例如媒体服务器。 -
NTFS 和 FAT:Windows 文件系统 -
HFS+:苹果系统的文件系统
查看以下 Ubuntu 系统上的文件系统:
执行命令:
df -T

三、Linux 下磁盘的分区
硬盘可以细分为多个分区,本质上就是多个块设备。回想一下这样的例子,/dev/sda1 和/dev/sda2, /dev/sda 是整个磁盘,但是/dev/sda1 是磁盘上的第一个分区。分区对于分离数据非常有用,如果你需要某个文件系统,可以轻松地创建一个分区,而不是将整个磁盘设置为一种文件系统类型。
分区表每个磁盘都有一个分区表,这个表告诉系统磁盘是如何分区的。该表告诉你分区的开始和结束位置,哪些分区是可引导的,磁盘的哪些扇区分配给哪些分区等等。有两种主要的分区表方案,主引导记录(MBR)和 GUID 分区表(GPT)
分区磁盘由帮助我们组织数据的分区组成。你可以在一个磁盘上有多个分区,它们不能相互重叠。如果有未分配给分区的空间,则称为空闲空间。分区的类型取决于你的分区表。在一个分区中,你可以拥有一个文件系统,也可以将一个分区专用于其他功能,如交换分区。
MBR:
-
传统的分区表,被用作标准 -
可以有主分区、扩展分区和逻辑分区 -
MBR 有四个主分区的限制 -
通过将一个主分区划分为一个扩展分区(一个磁盘上只能有一个扩展分区),可以创建额外的分区。然后在扩展分区中添加逻辑分区。逻辑分区的使用就像任何其他分区一样。 -
支持最大 2TB 的磁盘
GPT:
-
GUID 分区表(GPT)正在成为磁盘分区的新标准 -
只有一种类型的隔断,你可以做很多 -
每个分区都有一个全局唯一 ID (GUID) -
主要用于基于 UEFI 的引导
文件系统结构
文件系统是文件和目录的有组织的集合。在其最简单的形式中,它由一个管理文件的数据库和实际文件本身组成。
-
引导块:它位于文件系统的前几个扇区中,文件系统并没有真正使用它。相反,它包含用于引导操作系统的信息。操作系统只需要一个引导块。如果你有多个分区,它们将有引导块,但其中许多是未使用的。 -
超级块:这是在引导块之后的单个块,它包含关于文件系统的信息,例如 inode 表的大小、逻辑块的大小和文件系统的大小。 -
索引表:把它看作是管理文件的数据库。每个文件或目录在索引表中都有一个唯一的条目,并且包含关于该文件的各种信息。 -
数据块:这是文件和目录的实际数据。
让我们看一下不同的分区表。下面是一个使用 MBR 分区表(msdos)的分区示例。你可以看到系统上的主分区、扩展分区和逻辑分区。
执行:
sudo parted -l

磁盘分区
如果我们需要对磁盘进行分区。有很多工具可以做到这一点:
-
fdisk 基本的命令行分区工具,不支持 GPT -
parted 这是一个命令行工具,支持 MBR 和 GPT 分区 -
gparted 这是 parted 的 GUI 版本 -
gdisk fdisk 但不支持 MBR,只支持 GPT 我们用 parted 来划分。假设我连接了 USB 设备,我们看到设备名称是/dev/sdb2.
四、创建文件系统
执行命令:(-t ext4:指定文件系统类型为 ext4。)
sudo mkfs -t ext4 /dev/sdb2
就这么简单!mkfs(制作文件系统)工具允许我们指定我们想要的文件系统类型和位置。你只希望在新分区的磁盘上创建文件系统,或者在对旧磁盘重新分区时创建文件系统。如果你试图在现有文件系统之上创建一个文件系统,那么很可能会使你的文件系统处于损坏状态。
五、. mount and umount(挂载文件系统)
Linux 启动时,第一个必须挂载的是根文件系统;若系统不能从指定设备上挂载根文件系统,则系统会出错而退出启动。之后可以自动或手动挂载其他的文件系统。
在你可以查看文件系统的内容之前,你必须挂载它。要做到这一点,需要设备位置、文件系统类型和挂载点,挂载点是系统上文件系统将要附加的目录。我们要把设备挂载到一个挂载点。
首先创建挂载点,然后进行挂载:
mkdir /myTest
sudo mount -t ext4 /dev/sdb2 /myTest
现在当我们转到/myTest 目录时,我们可以看到文件系统的内容,-t 指定文件系统的类型,然后是设备位置,然后是挂载点。
从挂载点卸载设备:
sudo umount /myTest
或者
sudo umount /dev/sdb2
请记住,内核按照找到设备的顺序命名设备。如果我们的设备名称在我们挂载它之后因为某种原因改变了怎么办? 你可以使用设备的通用唯一 ID (UUID)而不是名称。
查询系统中块设备的 uuid:

我们可以看到我们的设备名称、它们对应的文件系统类型和它们的 uuid。现在,当我们想要挂载某个设备时,我们可以使用:
sudo mount UUID=59c03b23-fd61-4bfc-b9fa-570edf47a0b9 /myTest
大多数情况下,你不需要通过设备的 uuid 来挂载设备,使用设备名称要容易得多,而且通常情况下,操作系统知道挂载常见的设备,如 USB 驱动器。如果你需要在启动时自动挂载文件系统,就像你添加了一个辅助硬盘驱动器一样,可能会使用 UUID。
六、/etc/fstab(自动挂载文件系统)
当我们想在系统启动时自动挂载文件系统,我们可以将它们添加到文件系统表的缩写文件/etc/fstab 中。该文件包含已挂载的文件系统的永久列表。

每一行代表一个文件系统,字段是:
-
UUID -设备标识 -
挂载点 -文件系统挂载到的目录 -
文件系统类型 -
选项 -其他挂载选项 -
转储 -由转储实用程序用来决定何时进行备份,默认为 0 -
Pass -用来决定应该检查文件系统的顺序,如果值为 0,则不检查
要添加一个条目,只需使用上面的条目语法直接修改/etc/fstab 文件。修改这个文件时要小心,如果搞砸了,可能会给带来一些麻烦
其中 swap 属于交换分区,这个交换分区是什么? 交换分区是我们用来给系统分配虚拟内存的。如果内存不足,系统会使用这个分区将空闲进程的内存“交换”到磁盘,这样就不会陷入内存困境。
使用分区作为交换空间假设我们希望将/dev/sdb2 分区设置为交换空间。
-
首先确保我们没有任何东西在分区上 -
执行命令 mkswap /dev/sdb2 初始化交换区 -
运行 swapon /dev/sdb2 将启用交换设备 -
如果你希望交换分区在启动时保持不变,你需要在/etc/fstab 文件中添加一个条目。w 是使用的文件系统类型。 -
删除交换模块:swapoff /dev/sdb2
通常应该分配大约两倍于内存的交换空间。但是现在系统通常已经足够强大并且有足够的内存。
七、磁盘使用情况
你可以使用一些工具来查看磁盘的利用率: df 命令显示当前挂载的文件系统的利用率。-h 标志提供了一种可读的格式。你可以看到设备是什么,以及有多少容量被使用和可用。
df -h

假设你的磁盘快要满了,你想知道当前目录下哪些文件或目录占用了这些空间,你可以使用 du 命令。
du -h

八、文件系统修复
有时我们的文件系统并不总是处于最佳状态,如果我们突然关闭系统,我们的数据可能会损坏。这取决于系统是否试图让我们回到工作状态。
fsck(文件系统检查)命令用于检查文件系统的一致性,甚至可以尝试为我们修复它。通常,当你启动磁盘时,fsck 将在磁盘挂载之前运行,以确保一切正常。但有时磁盘非常糟糕,需要手动执行此操作。但是,一定要在你处于修复磁盘或可以在不挂载的情况下访问文件系统的地方执行此操作。
sudo fsck /dev/sdb1
九、索引节点
还记得我们的文件系统是如何由实际文件和管理这些文件的数据库组成的吗?该数据库称为索引节点表。
**什么是索引节点?**索引节点(inode)是该表中的一个条目,每个文件都有一个索引节点。它描述了关于文件的所有内容,例如:
-
文件类型-常规文件,目录,字符设备等 -
拥有者 -
拥有组 -
访问权限 -
时间戳- mtime(最后一次修改文件的时间),ctime(最后一次修改属性的时间),atime(最后一次访问的时间) -
文件的硬链接数 -
文件大小 -
分配给文件的块数 -
指向文件数据块的指针 基本上索引节点存储了关于文件的所有内容,除了文件名和文件本身
**什么时候创建索引节点?**创建文件系统时,也会为索引分配空间。有一些算法可以根据磁盘容量等确定需要多少索引节点空间。你可能在某些时候看到过磁盘空间不足的错误问题。同样的情况也会发生在索引节点上,你可能会耗尽索引节点,因此无法创建更多的文件
索引节点信息索引节点是由数字标识的,当一个文件被创建时,它被分配一个索引号,这个编号是按顺序分配的。然而,有时你可能会注意到,当你创建一个新文件时,它得到的索引号比其他文件低,这是因为一旦索引被删除,它们就可以被其他文件重用。查看索引编号执行 ls -li 命令:

该命令的第一个字段列出了索引号。你还可以使用 stat 查看有关文件的详细信息,它还会告诉你有关索引节点的信息。

十、符号链接
在 Windows 操作系统中,有一种叫做快捷键的东西,快捷键只是其他文件的别名。如果对原始文件做了一些操作,可能会破坏快捷方式。在 Linux 中,等同于快捷方式的是 符号链接(或软链接或符号链接)。symlinks 允许我们通过文件名链接到另一个文件。Linux 中的另一种类型的链接是 硬链接,它们实际上是另一个指向索引节点的链接文件

可以看到,符号链接用-> 表示。注意我是如何获得一个新的节点号的,符号链接只是指向文件名的文件。当你修改符号链接时,文件也会被修改。节点号对于文件系统是唯一的,你不能在一个文件系统中有两个相同的节点号,这意味着你不能通过节点号在不同的文件系统中引用文件。但是,如果你使用符号链接,它们不使用节点号,而是使用文件名,因此可以跨不同的文件系统引用它们。
硬链接
一般情况下,文件名和 inode 号码是一一对应关系,每个 inode 号码对应一个文件名,但是 Linux 系统允许多个文件名指向同一个 inode 号码,这就意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是删除一个文件名,不影响另一个文件名访问,相当于源文件的副本,这种情况叫做硬链接。
inode 信息中有一项叫做链接数,记录指向该 inode 的文件总数,这时会加 1,反过来,删除一个文件名,会减 1,当链接数变为 0 时,表明没有文件指向这个 inode 号码,系统就会回收这个 inode 号码与文件数据块区。
硬链接只是创建另一个文件,该文件具有到同一节点的链接。因此,如果我修改了 myFile2 或 myFile2link 的内容,两者都将看到更改,但如果我删除了 myFile2,则仍然可以通过 myFile2link 访问该文件。这就是 ls 命令中的链接计数发挥作用的地方。链接数是一个节点拥有的硬链接的数量,当你删除一个文件时,它会减少链接数。只有当节点的所有硬链接都被删除时,节点才会被删除。当你创建一个文件时,它的链接计数是 1,因为它是唯一指向该节点的文件。与符号链接不同,硬链接不跨文件系统,因为索引节点对于文件系统是唯一的。
