操作系统的文件系统

操作系统的文件系统

Apr 22, 2021
操作系统, Linux

文件是操作系统进行存储时使用最多的抽象之一,在 Linux 中,一切皆文件。本文以 Linux 为例,介绍下文件系统中的基础概念。

数据结构 #

每个文件实质上是一个有名字的字符序列,序列的内容为文件数据(file data),而序列的长度、序列的修改时间等描述文件数据的属性等其他信息称为文件元数据(file metadata)。

块(block) #

通常来说,文件系统将文件数据保存在存储设备中,操作系统将这些存储设备抽象为块设备(block device),以方便文件系统使用统一的接口访问,块设备上的存储空间在逻辑上被划分为固定大小的存储块(block),块是块设备读写的最小单元,大小一般为 512 字节或 4KB。每个存储块上均有一个地址,称为块号

索引结点(inode) #

除了块以外,文件系统还需要一个地方存储文件的元数据,在类 Unix 的系统中,这种区域就叫做 inode,即索引结点(index node),每个文件都有对应的 inode。最重要的,inode 记录了一个文件所对应的存储块号。在存储时,操作系统会将硬盘分为两个区域,一个是数据区,存放文件数据;另一个是 inode 区(inode table),存放inode所包含的信息,inode 的总数在磁盘格式化的时候就已经确定了。

inode 包含的信息有:

  • inode 编号
  • 文件的字节数
  • 文件拥有者的 User ID
  • 文件的 Group ID
  • 文件的读、写、执行权限
  • 文件的修改时间等
  • 文件的链接数,即有多少个文件名指向这个 inode
  • 文件数据 block 的位置

通过 stat 命令可以查看文件的 inode 信息:

$ stat -s Podfile
st_dev=16777231 st_ino=33838717 st_mode=0100644 st_nlink=1 st_uid=501 st_gid=20 st_rdev=0 st_size=1664 st_atime=1655259144 st_mtime=1655259140 st_ctime=1655259140 st_birthtime=1655259140 st_blksize=4096 st_blocks=8 st_flags=0

通过 ls -i test.txt 可以查看 inode 号,对于计算机来说,使用 inode 编号就可以找到对应的文件,但是对于用户来说,使用 inode 编号进行文件管理非常困难。于是操作系统增加了一层从文件名到 inode 号之间的映射。文件名不在 inode 中,而是存在目录中。

$ ls -i Podfile
33838717 Podfile

通过 df 命令可以查看每个硬盘分区的 inode 总数和已经使用的数量。

$ df -h

超级块(super block) #

硬盘除了会分成 inode 区域和数据块区域,还会分出一个超级块区域。一般来说超级块是一个文件系统存储布局中必不可少的结构,超级块中记录了整个文件系统的全部元数据,比如块个数、块大小、空闲块等。

超级块和 inode 加载进内存的时机是不同的:

  • 超级块 当文件系统被挂载时。
  • inode区 当文件被访问时。

目录和目录项 #

目录是一种特殊类型的文件,记录了从文件名到 inode 号的映射,目录本身也是文件,存储的是一系列目录项的列表,每个目录项由两部分组成:文件名和对应的 inode 号。

对目录的操作实际上是操作目录中的目录项:

  • 查找 在目录中查找某个文件时,文件系统从目录文件中第一个目录项开始,依次匹配文件名。
  • 遍历 与查找类似,依次检查目录项,并通过回调函数等方式返回结果。
  • 增加 当一个新的文件被创建时,文件系统会在其父目录中增加一个新的目录项,记录其所创建文件的文件名和 inode 号。
  • 删除 当一个文件被删除时,文件系统会从其父目录中删除对应的目录项。

硬链接和软链接 #

一般情况下,文件名和 inode 号码是"一一对应"关系,每个 inode 号码对应一个文件名。但是,Unix/Linux 系统允许,多个文件名指向同一个 inode 号码。这意味着,可以用不同的文件名访问同样的内容;对文件内容进行修改,会影响到所有文件名;但是,删除一个文件名,不影响另一个文件名的访问。这种情况就被称为"硬链接"(hard link)。

通过 ln 命令可以创建一个硬链接:

ln 源文件 目标文件

还有一种情况,文件 A 和文件 B 的 inode 号码虽然不一样,但是文件A的内容是文件 B 的路径。读取文件 A 时,系统会自动将访问者导向文件 B。因此,无论打开哪一个文件,最终读取的都是文件 B。这时,文件A就称为文件 B 的"软链接"(soft link)或者"符号链接(symbolic link)。

通过 ln -s 可以创建一个软链接:

ln -s 源文件 目标文件

软链接和硬链接的区别 #

  • 硬链接指向文件内容块的地址
  • 软链接指向同一文件名的地址
  • 删除源文件后软链接失效,硬链接依然有文件

软链接应用 #

  • 动态库版本管理
  • 快捷方式

硬链接应用 #

  • 文件多人共享
  • 文件备份
  • 从不同角度对文件进行分类

虚拟文件系统 #

在计算机系统中可能同时存在多种文件系统,比如用户可以使用 NTFS 和 Ext4 两个文件系统管理一块机械硬盘上的不同区域。而操作系统希望对用户提供一个统一的接口,于是在用户层和文件系统层之间引入了中间层,虚拟文件系统(Virtual File System,VFS)。

通过虚拟文件系统,用户就可以通过系统调用的方式操作“一切皆文件”的文件了,可以把这些文件分成三类:

  • 磁盘的文件系统 它是直接把数据存储在磁盘中,比如 Ext 2/3/4、XFS 等都是这类文件系统。
  • 内存的文件系统 这类文件系统的数据不是存储在硬盘的,而是占用内存空间,我们经常用到的 /proc/sys 文件系统都属于这一类,读写这类文件,实际上是读写内核中相关的数据。
  • 网络的文件系统 用来访问其他计算机主机数据的文件系统,比如 NFS、SMB 等等。

文件描述符 #

文件描述符实际上是一个整数,通常由 VFS 进行维护,VFS 为操作系统中的每个进程维护一个文件描述符表(也叫文件表),该表以文件描述符为索引,保存了一组文件描述结构。每个文件描述结构中记录了一个被打开文件的各种信息,如目标 inode(即被打开文件的 inode)、文件当前的读写位置和文件打开的模式等。

当 VFS 通过路径解析找到要打开的文件 inode 后,VFS 会分配一个文件描述符和对应的文件描述结构并进行初始化。VFS 将 inode 信息记录在文件描述结构中,将文件读写位置设为 0,并将文件打开的模式也记录在文件描述结构中,最后 VFS 将文件描述符返回给应用程序,表示文件已成功打开。

当应用程序使用文件描述符调用文件操作(read 或 write)时,VFS 使用文件描述符在该进程的文件描述符表中找到对应的文件描述结构,通过文件描述结构中保存的信息,VFS 可以继续执行应用程序所请求的操作。

文件描述符使得 VFS 无须每次都进行路径解析和各种检查操作,同时通过维护文件描述符表,操作系统可以监视和控制每个进程正打开的文件,从而对资源使用情况进行控制。

系统调用 #

Unix 系统 I/O 模型最为显著的特性之一是其 I/O 通用性 概念。操作系统提供了一系列接口供应用程序用来对文件进行访问,如打开(open)、读取(read)、写入(write)和关闭(close)。

open #

fd = open(pathname, flags, mode)

open 函数打开 pathname 所标识的文件,并返回文件描述符,用以在后续函数调用中指代打开的文件。如果文件不存在,open() 函数可以创建之,这取决于 flags 的设置,flags 还可以指定文件的打开方式:只读、只写或读写。

read #

numread = read(fd, buffer, count)

read 调用从 fd 说指代的打开文件中读取至多 count 直接的数据,并存储到 buffer 中。read() 调用的返回值为实际读取到的字节数,如果再无字节可读(比如读到文件结束符 EOF),则返回 0。

write #

numwritten = write(fd, buffer, count)

write 调用从 buffer 读取多达 count 字节的数据写入由 fd 所指代的已打开文件中,write 调用的返回值为实际写入文件中的字节数,并且有可能小于 count。

close #

status = close(fd)

在所有的输入输出操作完成后,调用 close() ,释放文件描述符 fd 以及与之相关的内核资源。

参考 #

https://xiaolincoding.com/os/6_file_system/file_system.html

现代操作系统:原理与实现

本文共 2867 字,上次修改于 Mar 25, 2024,以 CC 署名-非商业性使用-禁止演绎 4.0 国际 协议进行许可。

相关文章

» 常见的垃圾回收算法

» 操作系统的任务调度机制(四)通信、同步和死锁

» 操作系统的任务调度机制(三)调度器策略

» 操作系统的任务调度机制(二)进程和线程模型

» 操作系统的任务调度机制(一)演进历史