想象你生活的小区门口有一间“综合快递驿站”。顺丰、中通、京东、邮政都在驿站里有各自的货架,但你去取件时,只需要报出取件码,驿站工作人员就会把包裹递给你——你完全不用关心这个包裹是顺丰送来的还是中通送来的。驿站是什么牌子不重要,重要的是它把各式各样的快递公司统一成了“报取件码 → 拿包裹”这一个动作。
Linux 内核里的 VFS(Virtual File System,虚拟文件系统)就是操作系统世界的“综合快递驿站”。你的程序要读文件、写文件,只需要调用 read()、write() 这些统一接口,VFS 会自动把请求分发给背后真实的文件系统——ext4、xfs、ntfs 就像是不同快递公司,它们的底层格式千差万别,但 VFS 保证了你在用户态看到的永远是“文件路径 + 读写”这套统一玩法。这篇内容,我们就来拆开这家“驿站”的运营手册,看看它如何用四大登记册管理包裹,如何让多家快递公司和平共处,又靠什么玄机让高频取件快到飞起。
理解 VFS 是理解 Linux 存储栈的地基。AI 工程师做模型加载、训练数据读取、容器镜像管理时,遇到的卡顿和延迟问题,追根溯源往往都藏在 VFS 与页缓存的协作细节里。掌握它,你调优 IO 时不再靠猜。
可视化演示
展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。
本节配有交互动效(进入视口后自动加载,约 3KB)。
系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T
VFS概述
小区快递驿站刚开业时,只有顺丰一家。后来中通、京东陆续想入驻,老板发现:如果每来一家快递公司,就得给居民重新发明一套“取件暗号”,居民准得疯掉。于是老板决定——所有快递公司必须遵循统一规则:你送来包裹,我登记;居民来取件,报取件码,我按码找件。这套统一规则,就是 VFS 存在的意义。
在 Linux 的世界里,早期每种文件系统都提供自己的系统调用接口,程序员写代码得为 ext2、minix 分别适配,痛苦不堪。VFS 出现后,内核规定:所有文件系统必须实现一组标准操作——读、写、打开、关闭、创建、删除。用户进程只跟 VFS 打交道,就像居民只跟驿站前台说话,至于后台是哪个快递公司,完全不用关心。
从用户态看,一切皆文件。read() 一个普通文本文件、read() 一个 socket、read() 一个 /proc/cpuinfo,调用的系统调用一模一样。VFS 通过 file_operations 函数表把请求分发到不同实现:普通文件走 ext4,socket 走网络协议栈,proc 文件走内存中的虚拟数据。这种“同一个接口,背后千差万别”的设计,让 Linux 能同时容纳上百种文件系统。
驿站的统一规则,让居民只认前台、快递公司只认驿站,双方都省心。VFS 就是内核里那个永远微笑的前台,它不说话,但所有文件都从它手里过。
- 无 VFS 的适配成本每个应用都要为每个文件系统单独写一套读写代码,成本是两者数量的乘积。
- 有 VFS 的适配成本文件系统只适配 VFS 一次,应用也只面对 VFS 一套接口,成本变成求和。
- 对比结论当文件系统和应用数量都很大时,求和远小于乘积,这正是 VFS 降低整体复杂度的数学依据。
四大对象
驿站前台要管好包裹,手上得有四种登记册:第一本记“整个驿站的信息”(租了多大场地、合作了几家公司);第二本记“每个包裹的身份证信息”(谁寄的、多重、放在几号仓);第三本记“取件码和包裹的对应关系”;第四本记“居民当前手里拿着的取件凭证”。VFS 的四大对象,就是这四本册子。
第一本是 superblock(超级块),对应整个文件系统的全局信息:文件系统类型、块大小、总容量、挂载状态。每个文件系统实例只有一个 superblock,就像每个驿站分店只有一本总账。
第二本是 inode(索引节点),对应单个文件的元数据:文件大小、权限、所有者、时间戳、数据块位置。注意,inode 不存文件名——文件名在目录里,inode 只是文件的“身份证号”。
第三本是 dentry(目录项),它负责把路径中的一个名字映射到 inode。比如打开 /data/model.bin,内核要依次解析 /、data、model.bin 三个 dentry。dentry 不是磁盘上持久化的结构,而是内存中动态构建的“取件码对照表”。
第四本是 file(文件对象),代表一个“正在被打开的文件实例”。它记录了当前读写位置(offset)、打开模式(只读/可写)、引用计数。同一个 inode 可以被多个 file 引用——比如两个进程同时读同一个文件,各自的 file 对象互不干扰,但它们指向同一个 inode。
四本登记册各司其职:superblock 管全局,inode 管个体,dentry 管名字,file 管会话。驿站能高效运转,靠的正是这种清晰的职责划分。
/data/model.bin 这类深层路径时,内核要逐级解析目录项。每解析一层算一次 dentry 查找,所以总开销与路径深度成正比。路径越深,打开越慢,这就是为什么频繁访问的模型文件最好放在浅目录。- 路径组件数 n把路径按
/切分,/data/model.bin有三段,所以 n=3。 - 每层一次查找每一段路径都要在当前目录的 dentry 哈希表中查找一次,共 n 次。
- 线性复杂度表达总时间随路径深度线性增长,记为 O(n)。
注册机制
新快递公司想入驻驿站,第一步不是直接拉货来,而是先到物业办公室签协议、登记公司信息——公司名叫什么、主营什么业务、联系人是谁。签完协议,这家公司才算有了“入驻资格”。VFS 里的文件系统注册,干的就是这件事。
在 Linux 内核中,每种文件系统通过一个 file_system_type 结构体描述自己:文件系统名字(如 "ext4")、挂载时调用的回调函数、文件系统特有的标志位。内核维护着一张全局链表,调用 register_filesystem() 就把新类型挂到链表尾部,unregister_filesystem() 则把它摘下来。
注册和挂载是两码事。注册只表示“内核知道世界上存在 ext4 这种文件系统”,并不代表它被使用。就像快递公司签了入驻协议,但还没在驿站里占货架。常见的做法是:文件系统作为内核模块编译,系统启动时动态加载,加载过程自动执行注册函数。ext4 模块被 insmod 加载的瞬间,它在注册链表上有了名字,但真正开始工作,还得等挂载。
入驻协议签好,快递公司有了合法身份,接下来就要分配货架了。注册机制保证了“哪些公司可以进驿站”这件事有章可循。
- 已注册类型数 K比如内核编译了 ext4、xfs、ntfs 三种模块,K=3。
- 线性遍历链表最坏情况下要遍历整条链表才能找到目标类型。
- 常数时间插入新类型注册只做链表尾部插入,时间复杂度是 O(1)。
挂载流程
快递公司签完入驻协议,开始在驿站里搭货架。货架要放在哪个区域?怎么和已有的区域衔接?挂载(mount)解决的就是这个问题——它把一个文件系统实例“安装”到目录树的一个节点上,这个节点叫做挂载点(mount point)。
比如你有一块新硬盘格式化成 xfs,上面存着训练数据集。执行 mount /dev/sdb1 /data,内核干的事是:找到 xfs 的 file_system_type,调用它的挂载回调,读取硬盘上的超级块,构建起 xfs 的 superblock、根 inode 和根 dentry,然后把根 dentry 挂到 /data 这个目录 dentry 上。从此,访问 /data/model.bin 就自动进入了 xfs 的世界。
整个系统的挂载关系构成一棵挂载树:根文件系统是最早挂载的,之后每个挂载点都在这棵树上长出新分支。同一个设备可以挂载到多个挂载点(不同视角看同一份数据),不同设备也可以挂到同一目录的上下级。Linux 还通过 namespace 实现挂载隔离——容器就是靠这个能力,让每个容器看到完全不同的目录结构,互不干扰。
货架区域划好了,快递公司的包裹码得整整齐齐。居民来取件,驿站前台就得有一套标准的“取件流程”——这就是读写路径。
/data/model.bin 会匹配挂载点 /data 而不是 /,因为 /data 更长、更具体。这就是“最长前缀匹配”规则。- 候选挂载点集合所有已挂载的路径构成集合 Mounts,如 {
/,/data}。 - 前缀条件筛选path 必须以挂载点开头,
/data/model.bin同时满足/和/data两个前缀。 - 取最长者 argmax在满足条件的候选里挑字符串长度最大的那个,得到唯一归属挂载点。
读写路径
居民取件有一套固定动作:报取件码 → 前台查册子 → 找到货架 → 取出包裹 → 交到手上。文件读写也是同理。用户进程调用 open("/data/model.bin", O_RDONLY),内核沿着路径解析 dentry 链,最终拿到 inode,创建 file 对象和文件描述符,返回给用户。这个 file 对象就是那张“取件凭证”。
拿到 fd 后,read(fd, buf, 1024) 正式进入 VFS 大门。VFS 层不关心数据到底存在硬盘哪个扇区,它只做一件事:根据 file 对象指向的 inode 和文件系统类型,找到对应的“实际操作函数表”,把请求分发下去。ext4 的 inode 会调 ext4 的读页函数,xfs 的 inode 会调 xfs 的读页函数——这就叫多态分发。
写路径也很类似:write(fd, data, len) 进入 VFS → 分发到具体文件系统的写函数 → 把数据写入页缓存并标记为脏页 → 真正落盘交给后台线程慢慢干。最后 close(fd) 释放 file 对象,减少引用计数。整个流程中,VFS 像一个调度员,只负责接单和派单,具体怎么干是各文件系统的事。
驿站的取件流程环环相扣,每一步都有明确责任人。VFS 的分发机制保证任何文件系统接入后都走同一套流程,不搞特殊化。
- VFS 分发开销函数指针跳转和参数校验,约微秒级。
- 文件系统层操作inode 映射、日志等,几微秒。
- 缓存查找在 radix 树中查找数据页,几十微秒到亚微秒。
- 块设备 IO真正读写磁盘或 SSD,毫秒级。
页缓存
驿站发现一个规律:有些包裹取件特别频繁。与其每次都去快递公司的仓库翻,不如在店面里放一排“高频快取柜”——热门包裹直接放柜子里,报出取件码就能秒拿。页缓存(page cache)就是 Linux 内核的“高频快取柜”。
第一次读某个文件页时,数据从磁盘加载到内存中的页缓存;第二次再读同一页,直接从内存返回,不再碰磁盘。所有文件系统共用这一套页缓存体系,所以页缓存被称为“统一缓存层”。它由 address_space 结构管理,内部用 radix 树(新版内核演进为 xarray)按文件内偏移快速索引缓存页。
命中率是页缓存的核心指标。命中率越高,平均访问时间越接近内存速度。预读(readahead)策略会猜测你下一步要读哪些页,提前把数据塞进缓存;脏页则由内核的 flusher 线程在适当时机批量写回磁盘,攒一批一起写,避免频繁刷盘。
高频快取柜让热门包裹随到随取,页缓存让热门数据驻留内存。驿站和内核都明白:离用户越近的存储越快,但容量越贵,所以只能让高频数据走捷径。
- 命中率 h读请求命中的比例,范围 0 到 1。
- 命中项贡献比例 h 的请求走内存快速通道,耗时 T_cache。
- 未命中项贡献其余 1−h 的请求必须穿透到磁盘,耗时 T_disk。
- 期望值合成两项加权相加就是平均访问时间,权重正比于各自发生的概率。
多FS协作
一家驿站同时入驻顺丰和中通,货架各占一边,互不干扰。居民取件时不会关心快递来自哪家公司,前台也一视同仁。ext4 和 xfs 这两个 Linux 上最常用的文件系统,在 VFS 屋檐下就是这么共存的。
ext4 是 Linux 默认文件系统的老牌选手,成熟稳定,适合普通服务器和启动分区;xfs 擅长处理大文件和高并发写入,常用于大型存储服务器。它们的数据布局、索引结构、分配策略完全不同,但在 VFS 看来,它们都只是“实现了统一接口的快递公司”。
多文件系统协作的关键在于:inode 编号只在所属文件系统内部唯一,所以 VFS 必须用“文件系统句柄 + inode 编号”二元组来全局标识一个文件。跨文件系统的复制(cp 命令)需要先读出源文件数据再写入目标文件系统,这中间的搬运全靠 VFS 协调。挂载树让不同文件系统各有领地,读写路径让请求按领地分发——两者配合,ext4 和 xfs 就能在同一台机器上岁月静好。
多快递公司同店竞争,居民受益;多文件系统在 VFS 下协作,应用受益。统一接口让“最好的工具干最合适的活”成为可能——日志盘用 xfs,系统盘用 ext4,各有千秋。
- 固定适配成本 C每种文件系统实现同一组接口函数的代价,例如读写、打开、关闭等。
- 线性叠加N 种文件系统各自独立实现,总成本是单种的 N 倍。
- 收益对比如果没有 VFS,应用层也要付出同样的适配成本,总成本会变成乘积。
性能优化
驿站生意越来越火,取件码越来越多,如何在几百个快递柜里快速找到目标包裹?驿站的做法是:给快递柜编上树形索引,先按区号找,再按货架号找,几层下来就锁定了目标。内核的页缓存也用了同样的思路——radix 树索引。
radix 树是一种压缩前缀树,按文件内偏移快速定位缓存页。在 4KB 页大小的文件里定位第 N 页,只需沿树走若干层,时间复杂度是 O(log_k n),比线性扫描快几个数量级。大树底下好乘凉,一亿页缓存也能做到几次跳转查完。
另一个关键优化是 RCU(Read-Copy Update)机制。当多个进程同时读同一个文件时,页缓存的查找路径是只读的,RCU 让读者完全不加锁,写者需要更新时先复制一份再替换指针。就像驿站查快递单号的柜台,所有人都能同时查阅,只有改单号的人需要单独排队。
树形索引、无锁读、预读、直通模式的取舍,让 VFS 在“快”和“稳”之间找到平衡。驿站的前台可以同时服务一百个取件人,内核也可以让一百个进程同时读一个文件而互不阻塞。
- 分支因子 k每个树节点最多有 k 个孩子,内核中通常取 64。
- 树的高度n 个页在 k 叉树下,高度为 log_k n。
- 查找跳转数最坏情况沿树走到底,跳转次数等于高度。
AI 应用场景
训练数据集的混合存储加速
阶段:数据准备与加载
解决的问题:AI 训练数据往往分散存储——部分在本地 SSD(ext4),部分在网络存储(NFS)或对象存储挂载点(s3fs)。传统做法是写一层复杂的“数据路由层”判断文件在哪个存储,维护成本高且容易出错。VFS 天然支持多文件系统共存,DataLoader 只需要用统一路径访问,数据落在哪个文件系统由挂载树决定,代码零改动。
具体案例:某大模型训练团队把热数据放在本地 ext4 格式的 NVMe 盘上,冷数据挂载到远端的 xfs 存储服务器。训练脚本里统一使用 /data/train/ 路径,/data/train/hot/ 命中本地页缓存高速读取,/data/train/cold/ 自动走网络。VFS 的页缓存把反复读取的 token 数据留在内存,实测数据加载吞吐提升 40%。
容器镜像层的秒级分发
阶段:模型部署与镜像管理
解决的问题:Docker 镜像的 OverlayFS 也是 Linux VFS 体系下的文件系统,它把多层只读镜像层叠加成统一视图。模型镜像动辄几个 GB,传统方式每次启动都完整拷贝,浪费大量磁盘和启动时间。VFS 的共享挂载和页缓存让多容器共享同一份镜像层数据,只在写入时复制。
具体案例:推理服务用 Kubernetes 同时拉起 20 个模型副本。由于 OverlayFS 底层共享宿主机页缓存,20 个容器读同一个模型权重文件时,只有第一个容器真正从磁盘加载,其余 19 个全部命中页缓存,启动时间从分钟级降到秒级。
高频 embedding 查询的延迟优化
阶段:向量检索与推理服务
解决的问题:向量数据库加载 embedding 索引文件时,随机访问模式对 IO 延迟极其敏感。传统实现每次查询都直接 read 文件,频繁触发磁盘 IO。利用 VFS 页缓存的预读机制和缓存复用,热数据自动驻留内存,查询延迟大幅下降。
具体案例:某推荐系统用 faiss 加载 5 亿条 embedding 的索引文件(约 20GB),服务启动后第一次查询耗时 50ms,之后相同索引页的查询稳定在 0.3ms——这正是页缓存命中率从 0 爬到 95%+ 的过程。即便 faiss 进程崩溃重启,同类索引页仍留在页缓存中,预热时间缩短 80%。
Python 演示
class FileSystem: """模拟一个具体的文件系统实现,如 ext4 或 xfs。""" def __init__(self, fstype): self.fstype = fstype def read_disk(self, rel_path, size): return "<{}:{}:raw-disk-bytes>".format(self.fstype, rel_path) class VFS: """模拟 VFS 层:注册、挂载、解析和页缓存统一管理。""" def __init__(self): self.fs_table = {} self.mounts = {} self.cache = {} self.hits = 0 self.total = 0 def register(self, fs): self.fs_table[fs.fstype] = fs def mount(self, fstype, mountpoint): if fstype not in self.fs_table: raise ValueError("unknown fs type: " + fstype) self.mounts[mountpoint] = fstype def _find_fs(self, path): for mountpoint in sorted(self.mounts, key=len, reverse=True): if path == mountpoint or path.startswith(mountpoint + "/"): rel_path = path[len(mountpoint):] or "/" return self.fs_table[self.mounts[mountpoint]], rel_path raise KeyError("no mount point covers: " + path) def read(self, path, size=64): self.total += 1 if path in self.cache: self.hits += 1 return self.cache[path][:size] fs, rel_path = self._find_fs(path) data = fs.read_disk(rel_path, size) self.cache[path] = data return data def hit_rate(self): return self.hits / max(self.total, 1) def main(): vfs = VFS() ext4 = FileSystem("ext4") xfs = FileSystem("xfs") vfs.register(ext4) vfs.register(xfs) vfs.mount("ext4", "/data") vfs.mount("xfs", "/backup") for _ in range(4): print(vfs.read("/data/model.bin")) for _ in range(2): print(vfs.read("/backup/snapshot.safetensors")) print("hit_rate: {:.2%}".format(vfs.hit_rate())) if __name__ == "__main__": main()
vfs.register(ext4)快递公司签入驻协议,把 ext4 类型登记进注册表。vfs.mount("ext4", "/data")分配货架,把 ext4 实例挂载到/data目录。for mountpoint in sorted(self.mounts, key=len, reverse=True)按挂载点长度降序遍历,实现“最长前缀匹配”。if path in self.cache:页缓存查询:命中就直接返回,不再打扰底层文件系统。fs.read_disk(rel_path, size)未命中时才真正调用具体文件系统读磁盘。self.hit_rate()计算命中率,对应正文中的 h。
常见误区
误区我以为 inode 里面存的是文件名,因为 `ls -i` 看到 inode 号和文件名列在一起,就以为它们是同一个东西。
正解inode 是文件的“身份证号”,记录权限、大小、数据块位置等元数据,但独独不存文件名。文件名放在目录里,由 dentry 把名字映射到 inode。这就像快递驿站里,面单写着“取件人张三”,但张三本人身份证上并没有“张三”这两个字的存储位置——名字是标签,身份证是实体。ls -i 只是同时展示了文件名和它对应的 inode 号而已。
为何常见:因为在用户态看不到 dentry 和 inode 的区分,目录列表天然把“名字+元数据”显示在一起,初学者很容易混淆标签与实体。
误区我买了块新硬盘,以为 `mount` 一下就等于格式化,能把空白磁盘变成可用文件系统。
正解格式化(mkfs)是在磁盘上初始化文件系统结构,写入 superblock 和根 inode;挂载(mount)则是把这个已经格式化的文件系统“接入”目录树的某个挂载点,让内核能访问它。类比来说,格式化是快递公司建好仓库、贴好分区标签;挂载是仓库在驿站里正式开张、开始接单。没格式化的盘挂载不了,格式化后不挂载也访问不了,两者缺一不可。
为何常见:日常使用中“新硬盘插上 → 格式化 → 挂载 → 能用”三步紧挨着执行,操作者很容易把中间两步合并成一个模糊概念。
误区我认为页缓存是当前进程读文件时,内核给这个进程专门划出的一块内存,进程退出缓存就没了。
正解页缓存是全局统一的,属于整个内核地址空间,不属于任何单一进程。进程 A 读过的文件页,进程 B 再读也能直接命中;进程 A 退出后缓存还在,下一个新进程还能复用。驿站里的高频快取柜不是某个居民的私人储物柜,而是所有居民共享的公共设施。正因为全局共享,多个容器读同一个模型权重才能共同受益。
为何常见:进程的内存布局图里通常画着“文件映射区”,容易让人误以为页缓存是进程地址空间的一部分。
误区既然是统一缓存层,那所有文件 IO 一定都会经过页缓存,对不对?
正解大多数情况确实如此,但存在明确的例外。open() 时带上 O_DIRECT 标志,IO 直接绕过页缓存,在用户缓冲区与磁盘设备之间直接传输。数据库软件经常这么干,因为它们要自己控制缓存策略、避免双缓存浪费内存。还有像网络文件系统的一些读路径、某些设备文件操作,也不会走页缓存。快递驿站里确实“几乎所有包裹都进柜子”,但加了“加急直送”标签的包裹会直接从货架送到门口,不进柜。
为何常见:教材为了讲清楚页缓存这个核心机制,通常会省略 O_DIRECT 这条旁路,导致学习者形成“全部必经”的绝对化认知。
面试题
Q1. VFS 在 Linux 内核中的核心职责是什么?
- A.负责磁盘碎片的整理归并
- B.统一文件系统访问接口← 正确答案
- C.直接管理物理内存分配
- D.对文件内容进行加密压缩
答案:B。VFS 的核心是提供统一接口层,屏蔽底层不同文件系统的差异,让应用用同一套读写接口访问 ext4、xfs 等多种文件系统。它不负责磁盘整理、内存分配或加密,这些是其他子系统的职责。就像驿站统一了快递公司取件规则,但不管包裹内容。
Q2. 下列哪个对象记录文件的权限、大小和时间戳?
- A.superblock
- B.dentry
- C.inode← 正确答案
- D.file
答案:C。inode 是文件的“身份证”,存权限、大小、时间戳和数据块位置等元数据。superblock 管整个文件系统的全局信息,dentry 管路径名到 inode 的映射,file 代表一次打开的实例记录读写位置。
Q3. 页缓存命中的读请求数据来自哪里?
- A.磁盘的固定扇区
- B.内存中的缓存页← 正确答案
- C.CPU 的一级缓存
- D.网卡的接收队列
答案:B。页缓存是内核把磁盘数据留在内存的副本,命中时直接从内存返回,不再访问磁盘。CPU 一级缓存容量太小且不直接存文件内容,磁盘扇区和网卡队列与普通文件读无关。
Q4. dentry 的主要作用是什么?
- A.存储文件的全部数据内容
- B.把路径组件映射到 inode← 正确答案
- C.记录文件系统的总容量
- D.维护打开文件的读写位置
答案:B。dentry 是目录项,负责把一个路径名(如 data)映射到对应 inode,让路径解析得以逐级进行。它不存文件内容,superblock 记录全局容量,file 对象维护读写位置。
Q5. 执行 mount 命令时,内核主要完成什么操作?
- A.在磁盘上写入文件系统格式
- B.把文件系统实例挂到目录树节点← 正确答案
- C.给磁盘创建新的分区表
- D.清空目标目录里的所有文件
答案:B。挂载是把已格式化的文件系统实例接入目录树的挂载点,之后访问该目录就进入该文件系统。格式化是 mkfs 的事,分区是 fdisk 的事,挂载不会清空目录内容(只可能遮蔽原目录)。
Q6. 打开路径 /data/model.bin 时,路径解析按什么顺序执行?
- A.inode → dentry → superblock
- B.根 dentry → data → model.bin← 正确答案
- C.model.bin → data → 根 dentry
- D.一次哈希直接获得完整文件
答案:B。路径解析从根目录开始逐级向下,每层目录找一次 dentry。解析 /data/model.bin 先查根 dentry 下的 data,再在 data 里找 model.bin,共三次查找。不能反着查,也没有一次性哈希得到整个路径的办法。
Q7. 写文件时数据先进入页缓存并被标记为脏页,随后由谁负责真正落盘?
- A.调用 write 的用户进程同步写
- B.内核 flusher 线程异步回写← 正确答案
- C.磁盘控制器自动完成
- D.下一次 read 时顺带写入
答案:B。write 通常只更新页缓存并标记脏页,真正写盘由内核的 flusher 线程在后台异步执行,攒批刷盘提升效率。用户进程不会同步等磁盘写完,磁盘控制器不懂文件语义,read 和写盘无关。
Q8. VFS 的四大核心对象包括哪些?
- A.superblock← 正确答案
- B.inode← 正确答案
- C.dentry← 正确答案
- D.segment
答案:ABC。VFS 四大对象是 superblock(文件系统全局信息)、inode(文件元数据)、dentry(路径映射)、file(打开实例)。segment 是其他子系统概念,不属于 VFS 四对象。
Q9. 关于页缓存,下列说法正确的有哪些?
- A.被所有文件系统共享← 正确答案
- B.命中后可避免磁盘 IO← 正确答案
- C.只缓存已关闭的文件数据
- D.未命中时从磁盘加载数据← 正确答案
答案:ABD。页缓存是全局统一缓存,所有文件系统共用;命中时直接从内存返回避免磁盘访问;未命中才触发磁盘加载。它缓存的是最近访问的文件页,不管文件是否关闭,所以 C 错误。
Q10. 多文件系统能在 Linux 中共存协作,依赖哪些机制?
- A.统一接口抽象← 正确答案
- B.挂载树隔离边界← 正确答案
- C.页缓存统一管理← 正确答案
- D.所有文件系统共享同一数据块
答案:ABC。VFS 提供统一接口让不同文件系统同样工作;挂载树让各文件系统各有领地互不干扰;页缓存统一管理数据页让缓存全局共享。D 错误——各文件系统数据块布局完全不同,不共享数据块存储。