文件系统

文件系统介绍

文件系统(File system)是操作系统的一层,它将磁盘(或其他块设备)的块接口(Block interface)转换为文件、目录等。

文件系统的主要功能:

  • 磁盘管理(Disk management)

    将磁盘块集成文件。

  • 命名(Naming)

    接口通过名字访问文件而不是磁盘块。

  • 保护(Protection)

    保护文件数据安全。

  • 可靠性/耐用性(Reliability/Durability)

    保证文件的持久性。

文件

文件是一种对计算机上非易失性(non-volatile)存储的抽象。

其包括了:

  • 数据(Data)

    在硬件上存储的数据块。

  • 元数据(Metadata)

    • 用户(Owner)

    • 大小(Size)

    • 最近开启时间(Last opened)

    • 最后一次修改时间(Last modified)

    • 权限(Access rights)

      在 Unix / Linux 操作系统中,一般用 9 个 bit 来描述文件的权限,分别表示所属用户、群组用户和外部用户对该文件是否有读(Read)、写(Write)和执行(Execute)的权限。

    • 硬链接的数量(Number of hard links to the file)

    在 Unix / Linux 操作系统中,我们可以使用 ls 指令输出文件详细信息:

    1
    ls -l demo.txt

    终端输出:

    1
    -rw-r--r--@ 1 username  staff  0 10 12 16:38 demo.txt
    1. -rw-r--r--

    这部分显示了文件的权限模式。在这个权限模式中,有10个字符,它们按顺序表示了文件的不同权限。这个权限模式可以分为三部分:

    • 第一位是文件的类型, - 表示 demo.txt 是一个普通文件。
    • 接下来的 3 位(rw-)表示文件的所有者(username)具有读(r)和写(w)的权限,但没有执行(x)权限。
    • 再接下来的3位(r--)表示文件的组(staff)具有只读(r)的权限,但没有写入(w)或执行(x)的权限。
    • 最后的3位(r--)表示其他用户也具有只读(r)的权限,但没有写入(w)或执行(x)的权限。

    可以使用 chmod 指令对文件权限进行修改:

    1. 1 :这个数字表示文件的硬链接数。在这个示例中,文件 demo.txt 有一个硬链接。硬链接是指多个文件名指向相同的文件数据块。每个文件都有一个硬链接计数,当这个计数变为零时,文件数据才会被删除。

    2. username :文件所有者(Owner)的用户名。

    3. staff :文件所属组(Group)名。

    4. 0 :这是文件的大小,以字节为单位。在这个示例中,文件大小为0字节,表示这是一个空文件。

    5. 10 12 16:38 :这是文件的最后修改时间。具体来说,这个文件的最后时间是在 10 月 12 日的 16:38。

  • 文件的需求

    • 可变的大小(Variable size
    • 可实现多个并发用户和进程,同时能有保护作用(Multiple concurrent users / peocesses but with protection
    • 高效的查找技术(Being able to find files
    • 管理空闲磁盘块(Manage free disk blocks
文件系统的组成部分

Components of a File System

  • 目录结构(Directory Structure)

    目录结构是文件系统中用于组织和管理文件和目录的方式。通常,它采用树状结构(Hierarchy tree-like structure),类似于文件夹(目录)和文件的层次结构。根目录位于顶层,包含子目录和文件,这些子目录又可以包含更多的子目录和文件,以此类推。每个目录都可以包含文件和其他目录。

    Unix / Linux 操作系统使用的目录结构是一个树状结构,根目录通常表示为 /,然后有多个子目录和文件,如 /home/usr 等等。

    Files in the Linux root directory

  • 文件索引结构(File Index Structure)

    文件索引结构是文件系统用于管理文件和文件属性的内部数据结构。它包含有关文件的元数据信息,例如文件名、大小、创建日期、修改日期、权限和链接数等。文件索引结构的设计取决于文件系统的类型。

    例如,在 Unix / Linux 中,常见的文件系统如 Ext4 使用了索引节点(Inode)结构来存储文件的元数据。每个文件和目录都有一个唯一的索引节点号,而这个索引节点包含了关于文件的所有信息。这使得操作系统能够有效地查找和管理文件。

    我们可以给 ls 指令加上 -i 参数来显示文件的 inode 属性:

  • 数据块(Data Block)

    数据块是文件系统用来存储文件内容的基本单位。当文件太大以至于无法一次存储在内存中时,文件会被划分为多个数据块,每个数据块包含文件的一部分内容。这些数据块通常由文件系统管理,而文件索引结构中的信息将告诉操作系统如何组合这些数据块以获取完整的文件内容。

    在大多数文件系统中,数据块通常是一个连续的、固定大小的块,例如 4KB 或 8KB。文件系统会维护一个映射,将文件的逻辑块地址映射到物理磁盘上的数据块。

文件的启动与关闭

打开文件表

操作系统需要在内存维护所有开启文件的信息

打开文件表(Open file table)是操作系统内部用于跟踪已打开文件的数据结构,它包含了文件的元数据和状态信息,允许操作系统和进程有效地管理对文件的访问。每个正在运行的进程都有其自己的打开文件表,用于跟踪该进程所打开的文件。

  • 系统范围的打开文件表(System-wide open file table)

    系统范围的打开文件表是操作系统维护的一个数据结构,用于跟踪在整个系统范围内打开的文件。它存储了有关每个系统中已打开文件的信息(Information for every currently open file in the system)例如存储在 inode 属性中的信息(文件名、大小、所属用户等)

    系统范围的打开文件表允许不同的进程共享文件的访问信息。当多个进程打开同一个文件时,它们可以共享相同的系统范围的打开文件表条目,这意味着它们可以看到对该文件的更改。这有助于提高操作系统的效率,因为不需要为每个打开的文件创建独立的系统资源,只需在系统范围的打开文件表中引用相同的信息即可。

  • 进程内的打开文件表(Per-process open file table)

    每个进程都有自己的进程内打开文件表,用于跟踪该进程打开的文件。每个进程内的打开文件表包含了一个指向系统打开文件表的指针(A pointer to the system open file table) 和其他信息如文件描述符、文件状态标志、当前文件偏移量等。

    进程内的打开文件表使每个进程能够独立地管理其打开的文件,包括读取、写入和定位文件指针等操作。这确保了不同进程之间的文件访问彼此隔离,一个进程的文件操作不会影响其他进程。

open()close()

在 Linux 内核中,open()close() 是两个重要的系统调用函数,用于打开和关闭文件。这些函数在文件操作中起着关键作用,允许进程与文件进行交互。

一下展示两个系统调用在 C 语言中的接口:

  • open() 函数

    open() 函数用于打开文件,并返回一个称为文件描述符(File Descriptor)的整数值,该文件描述符在后续的文件操作中用于唯一标识打开的文件。函数原型如下:

    1
    int open(const char* pathname, int flags, mode_t mode);

    • pathname:是要打开的文件的路径或文件名。
    • flags:是一组标志,用于指定文件的打开方式,如只读、只写、追加等。常见的标志包括O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)、O_APPEND(追加写入)、O_CREAT(如果文件不存在则创建)、O_TRUNC(截断文件)等。
    • mode:是文件权限的设置,通常与 O_CREAT 标志一起使用,用于新创建的文件。(如果文件已存在则不需要传递该参数)

    open() 函数返回一个非负整数文件描述符,如果打开文件失败,则返回 -1。文件描述符在进程的打开文件表中唯一标识已打开的文件,可以用于后续的读取、写入和关闭操作。

  • close() 函数

    close() 函数用于关闭先前通过 open() 函数打开的文件,释放与文件描述符相关的资源。函数原型如下:

    1
    int close(int fd);

    • fd:是要关闭的文件描述符。

    close() 函数会关闭指定的文件描述符,使该文件描述符不再可用,同时释放与该文件描述符相关的资源,如文件表项和文件描述符表中的条目。关闭文件是一项重要的操作,它有助于避免资源泄漏和确保文件在不再需要时不会继续占用系统资源。

当我们打开一个文件

  1. 先进行 open() 系统调用,从磁盘中读取目标文件的 inode 信息,并存储在系统范围的打开文件表内。
  2. 当前进程内的打开文件表新增一个指向系统范围内打开文件表的索引(或指针),并通过 open() 函数返回给程序(在 Unix / Linux 中称为 File descriptor,在 Windows 中称为 File handle)。

当我们关闭一个文件

  1. 释放进程表项(Per-process table entry)
  2. 当前存储在此文件的内存缓存(Memory cache)中的任何数据都将在必要时写入磁盘。

文件的共享

一个文件可以被多个用户 / 进程访问,如果同一时间多个用户或进程以只读方式打开文件,那该过程可以正常执行,但如果多个用户或进程同时读写就可能引发冲突。

因此 OS 中引入了锁(Lock)

文件锁技术是一种用于控制对文件的并发访问的机制。它允许多个进程或线程协调共享对文件的访问,以防止竞争条件和数据损坏。文件锁通常用于多进程或多线程环境中,其中多个实体需要访问相同的文件。

有两种主要类型的文件锁:共享锁(Shared Lock)和独占锁(Exclusive Lock)。这些锁可以在文件上的不同部分或整个文件上设置。

  1. 共享锁(Shared Lock)
    • 多个进程可以同时持有共享锁。
    • 共享锁允许多个进程同时读取文件,但阻止其他进程获得独占锁。
    • 共享锁通常用于并发读取操作,以防止竞争条件。
  2. 独占锁(Exclusive Lock)
    • 仅一个进程可以持有独占锁。
    • 独占锁阻止其他进程获得任何类型的锁,共享锁和独占锁都被阻止。
    • 独占锁通常用于写入和修改文件的操作,以确保数据的一致性。

例如,用户 A 在读取文件时每个进程都持有共享锁,其他进程无法对文件内容进行修改,只能以只读方式打开文件。

文件索引与文件信息

Unix/Linux Inode

在 Unix 和 Linux 文件系统中,"inode"(索引节点)是一个关键的数据结构,用于管理和存储文件的元数据信息。每个文件和目录都有一个唯一的 inode,用于跟踪文件的属性、权限、所在位置等等,我们可以认为一个文件的 inode 主要维护了以下两类信息:

  • 元数据(Metadata)
  • 数据块地址(Which disk blocks belong to which file)

元数据在上文中已经介绍,包括文件的一些基本信息,数据块地址则是文件实际内容的存储地址。

我们可以使用 stat 指令查看文件包括 inode 在内的各种详细信息:

1
stat filename

示例

Inode 存储在什么地方?

在早期的 UNIX 系统中,它们被存储在磁盘。

  • Inode 不在数据块附近存储。要读一个小文件,先查找索引节点,再查找回数据。(Poor performance
  • 如果外部磁盘损坏,意味着文件系统会丢失。(Poor reliability

在后来的系统中,索引节点分布在磁盘块组中,更接近数据块本身

文件链接

文件链接允许我们创建一个文件的副本,即一份文件有两份不同名的实例,文件链接有以下两种:

  • 软链接(Symbolic (soft) links)

    保存原文件的路径,类似于 Windows 中的快捷方式。

    1
    ln -s original link

  • 硬链接(Hard links)

    链接文件与原文件拥有同样的 Inode 编号但需要注意的是目录是无法创建硬链接的

    1
    ln original link

    如上图,(a) 表示文件初始状态;(b) 表示创建了一个硬链接,此时文件的硬链接数变为 2;(c) 表示文件原来所属用户将文件删除,此时文件的硬链接数变为 1。

Unix 目录

目录(Directory)是一种特殊的文件,其包含:

  • 文件名列表(A list of filenames)
  • 指向 Inode 的指针(Pointers to inodes)

We are used to thinking about a directory containing files. This is really an illusion. Directories do not contain files. The data of the files is not stored in the directory.
A directory is really just a file. It's a special file with special rules (you can't just type cp /dev/null directory to erase it. It's got special bits to make sure a mere mortal can't mess it up. Because if a file system gets corrupted, then you can say goodbye to your data. On older UNIX systems, you actually could "read" the contents, using cat . , of a directory. But let me get back to that in a second...
A Unix file is "stored" in two different parts of the disk - the data blocks and the inodes. (I won't get into superblocks and other esoteric information.) The data blocks contain the "contents" of the file. The information about the file is stored elsewhere - in the inode.
Above all, the directory is just a table that contains the filenames in the directory, and the matching inode.

文件分配

连续分配

连续文件分配(Contiguous File Allocation)即所有文件在存储空间内是连续存储的,在文件分配表中所有文件只需要两个值来维护:

  1. 起始位置(Start address)
  2. 数据大小(Length)

这种分配方式在 CD 和 DVD 中常用。

这样的分配方式的优点就是易于实现,缺点也很明显,就是数据块之间存在许多未分配的小空间,这里我们称为外部碎片(External fragmentation),另一个就是其不能完全满足文件大小动态增长的需求,例如文件过大则可能超过预先分区的大小。

链式分配

链式分配(Linked Allocation)是一种文件存储分配方式。在链式分配中,文件中的数据块不是连续分配的,而是通过链表来链接。每个数据块都包含一个指向下一个数据块的指针,创建一个链表结构,使文件的数据块按照其实际分配的顺序链接在一起。

FAT

FAT(File Allocation Table)是一种经典的文件系统结构,通常用于存储媒体,如硬盘驱动器、闪存驱动器和其他可移动存储设备。

在 FAT 文件系统中,每个文件都由一系列链接在一起的簇来表示。文件的起始簇被存储在文件的目录项中,而接下来的簇号在 FAT 表格中找到。这样,文件的数据块通过 FAT 表格中的指针链起来,形成一个链表结构。

在 FAT 中,未被使用的数据块也会被放在一个链表中,称为 FAT free-list。

因此当我们格式化磁盘后,会发生以下两件事:

  1. 清空所有数据块的信息
  2. 将所有数据块链接为 free-list
  • 优点:易于实现
  • 缺点:对于大型文件而言,FAT 访问需要跳转许多数据块,可能造成过大的时间开销
Bitmap

操作系统需要管理未分配区域(Free space),除了上文提及的 FAT 中的 free-list 之外,还有另外一个常见的解决方案,就是使用 Bitmap

Bitmap 是一个数组,其维护了每个数据块是否被使用,如果该数据块被使用了,则为 1,否则为 0。

Bitmap

优点

  • 对任意一种存储方式都适用
  • 只需要很小的存储空间

常用文件系统

  • Unix / Linux

    • EXT 2/3/4

      EXT(Extended File System)广泛用于 Linux 和 Unix 操作系统家族,特别是在 Linux 系统中。它有多个版本,其中 EXT2、EXT3、和 EXT4 是最常见的。

    • ZFS

      ZFS(Zettabyte File System)是一种先进的、先进的文件系统和存储管理系统,最初由 Sun Microsystems(现在是 Oracle Corporation 的一部分)开发。

  • Apple

    • HFS+

      HFS+(Hierarchical File System Plus)最初由 Apple Inc. 开发并用于 Macintosh 计算机。它是 HFS(Hierarchical File System)的升级版本,引入了许多改进和新特性。

  • Microsoft

    • FAT 16/32

      主要在 MS-DOS 和 Windows XP 中使用。

    • NTFS

      NTFS(New Technology File System)是一种现代的文件系统,最初由 Microsoft 开发并引入到 Windows NT 操作系统中。

    • exFAT

      exFAT(Extended File Allocation Table)由 Microsoft 于 2006 年引入,旨在解决一些早期文件系统(如 FAT 32)存在的限制和问题。exFAT 主要设计用于存储在可移动存储介质(如闪存驱动器、SD 卡、外部硬盘等)上的大容量文件,尤其是用于跨平台数据交换。


文件系统
https://goer17.github.io/2023/10/06/文件系统/
作者
Captain_Lee
发布于
2023年10月6日
许可协议