文件系统概述
文件系统概述
复习定位
文件系统负责管理和组织磁盘上的数据——用户的程序和数据必须以文件的形式保存在磁盘上。操作系统提供统一的文件系统接口(open/read/write/close)屏蔽了下层磁盘的物理细节。理解文件系统的逻辑结构和物理组织——对于选择文件结构(连续/链式/索引)了解它们各自的优缺点至关重要。
文件系统的基础概念
文件是逻辑上相关的数据的集合——用文件名标识——操作系统通过文件名将文件映射到磁盘上的位置。用户可以创建、修改、删除文件——不必关心磁盘上的块是如何分布的。
系统为每个文件维护一个文件控制块(FCB)——存储文件的信息——文件名、文件类型、文件大小、创建时间、修改时间、文件权限、存储块地址等。在Linux中——这些元数据存放在inode结构中——目录项只存储文件名和对应的inode号。
文件的逻辑结构
无结构文件(流式文件)——文件是一个有序的字节流——操作系统不关心文件内部的结构——如.txt文件、.exe文件的二进制。这是现代操作系统通用文件形式。
有结构文件(记录式文件)——由固定长度的记录组成——如数据库的记录文件——每个记录具有若干字段。操作系统按照记录号和记录长度定位到一个具体的数据记录。
文件目录
目录本身也是一个文件——存储着"文件名→inode号"的映射关系。早期的单级目录结构——所有文件在同一目录下——不允许重名——查找效率低——现代操作系统使用多级目录(树形目录)结构——每个文件路径唯一——不同目录允许相同的文件名存在。
文件的物理结构
文件在磁盘上的存储方式:
连续分配——文件占一组连续的磁盘块。优点是顺序读写快——随机访问快(只需一次地址计算)。缺点是外碎片——删除文件后留下空洞——后续新文件不一定能填上(动态分区中的外碎片问题)。文件扩展困难——需要预先知道文件最终大小——不适合动态增长的文件(日志或数据库表)。
链接分配——文件数据块通过指针串联——类似于链表。每个块中有一个字段指向下一块的地址。解决了外碎片问题——文件可以动态增长。致命的缺点是随机访问——要读取文件的第n个块需要沿着链表依次遍历前n个块——无法按照数组的快路径做到O(1)定位。
索引分配——为每个文件分配一个磁盘块作为索引块——存放该文件所有数据块的地址指针。随机访问——读取第i块只需遍历索引块中的第i个指针(只经过索引的寻道而不是扫描前面所有的块)。文件很大时—需要多级索引(如UNIX的inode)——直接块12个(小文件没有任何间接寻址开销)+一级间接块+二级间接块+三级间接块。
空闲空间管理
位示图(bitmap)——用一个二进制位表示一个磁盘块是否空闲——1表示空闲、0表示占用。位图大小=磁盘块数/8字节——对1TB的磁盘、4KB块——位图大小=1TB/4KB/8=32MB——可以常驻内存——查询和分配回收都很快速。
空闲链表——将所有空闲块用指针串成一个链表——分配时从头取一块——回收时加到末尾。简单——但链表碎片化导致不利于连续分配。
复习检查
文件控制块FCB中存在哪些关键信息——目录项只存储文件名和inode号——inode中存储的是(文件大小/权限/时间/数据块指针等)——两者为什么被分开?
连续分配的外碎片问题是否类似于动态分区——文件删除后产生碎片——新文件可能占据不了一个连续的完整区域——而链接分配为什么会解决这个问题(因为数据块通过指针任意链接不需要连续地址)?
索引分配中UNIX inode的多级索引是如何支持大文件而不为小文件增加开销——直接块的数量(12个)足够覆盖大多数小文件——所以92%以上的小文件只需直接块定位——不需要额外的磁盘索引块I/O。
位示图的尺寸如何计算——如果磁盘为2TB、块大小4KB——位示图占据多少空间?
一个文件系统的inode数量总共有限——创建足够个数的小文件时可能出现"inode耗尽"而无法创建新文件——即便磁盘还有剩余空间——为什么?
文件系统的基本概念详解
文件系统是操作系统中负责管理持久数据存储的核心子系统。它负责将磁盘上的一系列物理块组织成层次化的文件和目录——使用户和应用程序可以通过文件名直接访问数据——而不需要知道数据在磁盘上的物理位置。
文件系统需要解决的核心问题包括:
- 文件的按名存取——通过目录结构从文件名定位到数据的物理位置
- 空间分配与回收——为新文件分配磁盘块——删除文件时回收已分配的块
- 数据完整性——确保文件系统的元数据和文件数据在系统崩溃后的一致性
- 并发访问控制——多个进程同时访问同一文件时的互斥和数据一致性
- 访问权限控制——不同用户对文件的读写执行权限的管理
文件的逻辑结构与物理结构的区别
文件的逻辑结构(用户视角)是数据的组织方式——流式文件(无结构文件)——文件是字节序列——应用程序自行解释文件内容结构。记录式文件——由固定长度的记录组成——每个记录有多个字段——数据库文件是典型的记录式文件结构。
文件的物理结构(实现视角)是数据在磁盘上的存放方式——连续分配/链接分配/索引分配决定了文件占用的磁盘块如何组织和寻址。操作系统负责将逻辑结构的文件通过物理结构的文件映射到具体磁盘块。
目录实现方式的演进
单级目录——所有文件在同一个目录下——命名冲突严重——查找效率低(O(n)——扫描所有目录项)。早期的CP/M和MS-DOS 1.0使用单级目录。
两级目录——每个用户一个独立的目录——用户间命名不冲突——但用户内部仍只能平铺所有文件——无法实现项目/功能分类。
多级目录(树形目录)——现代操作系统都使用的目录结构——目录项包含指向子目录或文件的索引节点——路径名唯一标识一个文件。每个进程有当前工作目录(CWD)——文件操作针对当前目录的相对路径查找。
无环图目录——允许一个文件或目录同时在多个目录中出现(硬链接/符号链接)——实现文件共享——但目录图必须保证无环以避免重名冲突。
文件共享与硬链接的实现
Unix/Linux中——硬链接允许一个文件有多个路径名——多个目录项指向同一个inode——使文件共享。删除一个路径名时只将inode的链接计数减1——只有链接计数变为0时——系统才实际释放该文件的数据块和inode。硬链接不能跨越文件系统——因为inode号只在同文件系统内唯一。
软链接(符号链接)则是一个独立文件——内容是被链接文件的路径字符串——可以跨越文件系统和不同设备、也可指向目录。当打开符号链接时——内核自动解析链接文件的内容——将操作重定向到目标文件路径。如果原文件被删除——符号链接指向一个不存在的路径——访问时产生"文件不存在"错误。
文件操作的系统调用流程
open("/home/user/file.txt", O_RDONLY):
1. 解析路径——从根目录/开始——先读取根目录的inode→数据块
2. 在根目录的数据块中查找"home"目录项的inode号
3. 读取home目录的inode和数据块——在其中查找"user"目录项
4. 读取user目录的inode和数据块——在其中查找"file.txt"目录项
5. 获取file.txt的inode号——从inode中读取文件属性(权限、大小、数据块指针)
6. 检查访问权限——比较当前用户的UID和文件的owner/group/other权限
7. 如果权限允许——在当前进程的文件描述符表中分配一个空闲fd——指向file的file结构
8. file结构记录文件状态(当前文件偏移、打开模式等)——返回fd给用户程序这一串路径解析过程是文件操作的基本路径——每次open都涉及多次目录项的查找——由VFS层递归完成——现代操作系统通过dentry cache(目录项缓存)加速这一解析过程。
复习检查(续)
文件系统的核心功能——按名存取(目录→inode→数据块)——空间分配(为新文件分配磁盘块)——并发访问控制(多个进程同时访问同一文件)——访问权限控制(用户/组/其他)——数据一致性(崩溃后恢复)。
文件的逻辑结构(流式/记录式)与物理结构(连续/链接/索引)的区别——逻辑结构是用户视角的数据组织方式——物理结构是磁盘上数据块的存放方式——操作系统通过文件系统实现从逻辑到物理的转换。
多级目录(树形目录)相比单级/两级目录的优势——允许命名空间在层次上独立——不同目录中可以有同名文件——路径解析清晰地定位文件位置——支持更复杂的文件组织。
open()系统调用的路径解析过程——从根目录开始逐级查找目录项——获取目标文件的inode——检查权限——分配文件描述符——返回给用户进程。
dentry cache在路径解析中的作用——缓存最近访问过的目录项——避免每次open都从磁盘读取目录数据块——加速路径解析过程。
文件系统中的权限与保护机制
Linux文件权限基于UGO(用户/组/其他)模式——每个文件维护所属用户(owner)、所属组(group)——以及三组独立的rwx(读/写/执行)权限位。
-rwxr-xr-- 1 alice dev 2048 Jun 29 10:00 script.sh第一个字符-表示普通文件——后面三组三位:rwx(所有者alice可读写执行)、r-x(dev组成员可读可执行)、r--(其他用户只能读)。
该文件还受SELinux/AppArmor的MAC(强制访问控制)约束——即使所有者授权其他用户读写——MAC策略可以覆盖这一授权并阻止访问。
文件系统的高可用与分布式文件系统
当单机文件系统的容量或吞吐遇到瓶颈时——需要采用分布式文件系统(NFS、GlusterFS、Ceph、Lustre)——将数据分布到多个存储服务器上——通过网络统一访问。分布式文件系统需要解决单机文件系统不需要处理的挑战:网络延迟、部分节点故障、数据一致性(跨节点)、元数据分布式管理。
其中——NFS(Network File System)将远程文件系统挂载到本地目录——使本地程序可以透明地读写远程文件。Ceph提供对象、块和文件三种接口——通过CRUSH算法(无集中元数据服务器)将数据分布到集群中的所有节点——提供高可扩展性和可靠性。
文件系统的空间占用监控与管理
操作系统中——文件和目录的存储空间占用通过df和du命令监控:
df -h # 显示各文件系统的总容量/已用/可用/挂载点
df -i # 显示inode的使用情况(防止inode耗尽)
du -sh /var/log # 显示/var/log目录的总大小
du -ah / | sort -rh | head -10 # 找到系统中最占空间的10个文件监控文件系统的空间使用是系统管理员的重要日常任务——特别是日志文件(/var/log/)、数据库数据目录和容器镜像存储目录——需要设置定期日志轮转(logrotate)和空间告警阈值以避免因磁盘满导致服务故障。
复习检查(续二)
Linux文件权限UGO模式的含义——user/group/other三组rwx权限的控制范围——rwx对普通文件和对目录的含义不同(目录的x表示可进入)。
df -i监控inode使用率的重要性——即使磁盘还有空闲空间——如果inode耗尽——也无法在文件系统中创建新的文件或目录——这是因为文件系统格式化时预设了inode数量上限。分布式文件系统相比单机文件系统的额外挑战——网络延迟、节点故障处理、跨节点数据一致性、元数据分布式管理——这些是单机文件系统不需要面对的问题。
NFS通过挂载远程目录实现透明访问——本地程序通过标准的
open/read/write系统调用访问远程文件——VFS层将请求转发到NFS客户端内核模块——NFS客户端通过RPC与NFS服务器通信。du -sh和df -h输出的差异——du从文件系统的目录树角度统计文件占用的空间——df从文件系统的块设备分配角度统计已用的块——两者在某些场景下不一致(如文件已被删除但进程仍持有打开的文件描述符——df显示有空间被占用但du找不到)。
文件的并发控制——记录锁
在多进程同时访问同一文件时——可能出现并发写入交叉的问题。文件锁用于协调多个进程对同一文件的读写:
建议性锁(Advisory Lock):
锁由应用程序主动检查和获取——不强制其他进程遵守——但所有遵守同一约定的进程可以通过`flock`或`fcntl`系统调用加锁解锁——实现协作式的并发控制。
强制性锁(Mandatory Lock):
由操作系统内核强制实施——只要一个进程持有了文件锁——其他进程对文件的读写操作都会被阻塞——直到锁被释放(但Linux默认使用建议性锁——只有挂载时指定`mand`选项才启用强制锁)。记录锁是文件锁的一种——允许进程锁定文件的一部分(从指定起始位置开始的特定字节范围)——而不是锁定整个文件——多个进程可以同时读写同一文件的不同部分——提高了并发度。
文件系统的配额管理
在多用户系统中——文件系统配额(quota)限制每个用户或每个组的磁盘使用量——包括空间使用(容量)和inode使用(文件数量)。超过配额的写入操作被拒绝——从而防止某个用户耗尽磁盘空间影响其他用户的正常使用。
# 配置用户配额示例
quota -u alice # 查看用户alice的配额使用情况
edquota -u alice # 编辑用户alice的配额限制
setquota -u alice 500M 600M 0 0 /home # 设置软限制500M/硬限制600M配额管理对于共享托管服务器、大学/公司文件服务器等场景非常重要。