发布时间:2026/9/5 9:25:25
从双击到内核:一次文件打开背后的操作系统原理 从双击到内核一次文件打开背后的操作系统原理文件系统的基本全貌宏观视角1.1 文件与文件系统文件也有一些分类。按逻辑结构分类如下所示。图1 文件逻辑结构分类标题文件系统首先包括的当然是当前存储容器中的所有文件。如果仅仅是一摞文件丢在存储容器中就像我的房间一样是非常乱的作为用户的我无法很好的利用这个文件。所以文件系统里头也得包括管理文件的功能这样当用户需要找寻文件的时候直接调用文件系统提供的接口即可获取文件。就像我在我的房间安排一个管家管理我的文件一样我只需要吩咐管家我要打开文件名为xxx的文件管家就可以把该文件递到我手中了。图2 文件系统组成值得注意的是在我们的windows个人电脑中一个物理硬盘可以分为几个逻辑硬盘ABCD。一个D盘对应一个文件系统一个C盘对应另一个文件系统也就是说D盘和C盘对应了两个独立的文件系统就好像在家里我和弟弟的房间是独立的两间管家也是独立的两个人。而这两个管家可能出自同一公司也可能出自不同公司这就是具体而言文件系统的不同了。当然如果属于同一个物理硬盘那他们就是同一种文件系统不过相互之间也是独立的就像两个来自同一公司的相互独立的管家。图3 逻辑硬盘与文件系统的关系1.2 文件系统的层次结构文件系统并不是独立于操作系统、位于操作系统之下的东西而是操作系统内核中的一个重要子系统负责文件的组织、存储、访问和管理。具体的文件系统有FAT文件系统(file allocation table)ext2文件系统extension:linux常用的文件系统NTFS文件系统(New Technology File System,windows的默认文件系统)这些具体的文件系统都是操作系统内核中的实现或可加载模块它们向上通过系统调用接口为用户程序服务向下通过设备驱动访问磁盘。因此每个文件系统具体的管理方式是不同的不过其管理哲学还是相似的。由于实现者不同其提供的接口当然也是不一样的那么操作系统为了屏蔽文件系统之间的不同就提供给了这些文件系统一套接口格式。这就是虚拟文件系统“你们这些文件系统如果不靠我要求的接口格式来实现接口【也就是函数名(实参)】,那你就别想接入我的操作系统了。”学到这我就想到难怪一个应用不能同时上架到ios和安卓原来是操作系统搁这设置了不同的门槛呢也就是设置了不同的接口规范。作为文件系统的开发者我必须写两份逻辑相同格式不同的代码才能分别接入 Windows 和 Linux。图4 虚拟文件系统文件系统的层次笔者认为非常抽象而且这些层次虽然看起来相互独立但有时不免重叠。层次结构从上至下分别是用户接口(用户调用)-文件目录系统就是一个目录以及其管理方案-存取控制模块权限控制-逻辑文件系统与文件信息缓冲区管理inode与逻辑块-物理文件系统将逻辑地址转换为物理地址-设备管理模块cpu与设备交流的中介-设备。具体内容暂且按下不表。图5 文件系统的层次结构1.3 逻辑磁盘前面也提到了在我们的笔记本电脑上windows操作系统中一个物理硬盘会被分割为多个逻辑磁盘也叫卷。所以你一打开我的电脑就会有好几个目录他们分别对应物理硬盘上连续的一段区域相互之间是独立的文件系统。而linux中一个操作系统就只对应一个根目录。私以为我们可以把一个windows系统下的CDE盘对应到linux中的根目录/就相当于windows中有好几个根目录而linux中只有一个根目录。图6 windows与linux目录定义的差异小补充对用户而言打开根目录看到的bin、home、usr等文件夹可能来自物理上完全不同的磁盘但操作系统通过挂载机制将它们“拼”成了一棵统一的目录树用户根本感知不到底层有几块硬盘。挂载就是将某个硬盘放到linux中的某个目录之下而不独立出来保证整个操作系统只有一个根目录。目录结构2.1 树形结构我们熟悉的目录结构是树形目录结构这是最最最常用的目录结构目前笔者还没见过其他的目录结构呢。而在文件系统的发展中还包括其他的目录结构分别是单目录结构双目录结构以及在树形结构的基础上发展的有向无环图目录结构。单目录和双目录可太好理解了就是一个文件系统中只有一个目录或者两个目录而这个目录之下就全是文件没有目录了。这两种层次结构最大和最明显的弊端就是同一个文件系统下允许重名的文件可太少了。而树形结构就允许目录之下有目录也有文件就像我们现在使用的这样可以无限套娃存储文件。有向无环图目录结构是在树形结构的基础上为了实现“共享”而发展的不过由于它实现的共享有点瑕疵bug所以就没有被广泛使用这里等我们介绍了inode和文件打开表后在文件共享中详说。接下来我们谈论的内容都建立在树形目录之下。2.2 目录项我个人认为教科书上对文件目录的描写太晦涩了其实是很简单的东西但是为了严谨就不得不用一些专业词汇套来套去给套复杂了。书中首先强调了目录也是一种文件。这是显然的但是有点弯绕的原因就是我们理解文件这个词的时候其实是有两层含义的宏观的文件就是文件系统管理的单位既包括微观的文件也包括目录。微观的文件就是那些带有文件后缀的文件(当然有一些不带文件后缀咱们可以理解成它把文件后缀省略掉了)。我寻思就应该起个别名比如宏观文件叫文件单位微观文件叫后缀文件anyway这只是我个人的牢骚。目录中的目录项其实就对应着我们打开一个目录后显示的每一行文件只是目录项中每一列的数据大部分都被系统省略了作为用户乍一看只能看到文件名。要是想看到其他部分列的数据咱们可以右键点击属性能够查到文件的详细信息不过文件的物理地址就被系统隐藏了没必要咱也看不懂。对于目录项我们只需要把目录项中的列从宏观上分为三列文件名文件详细信息文件的物理地址。图7 目录与目录项2.3 补充绝对路径与相对路径相对路径就是相对当前路径的文件位置绝对路径就是相对根目录的文件位置。比如绝对位置C:\Users\yufeng\language.txt其相对yufeng这个目录的文件位置就是language.txt。目录查询的方式线性搜索当我们在搜索框输入我们想要查询的文件的路径时系统会在咱们这个文件所属的目录下根据文件名逐项比对每一个目录项的文件名匹配成功则查询完成进行下一步操作。查询的时间复杂度是O(n)。文件数量为n哈希表将文件名根据哈希计算放入一个确定的位置或者这个确定的位置的周围如果这个计算出的确定位置被占据的话那我们查询文件的时候就会定位到这个文件实际存储位置的周围然后线性搜索匹配文件名查询成功缩短查询消耗的时间查询的时间复杂度接近O(1)。inodeinode的出现是专门为了解决一个问题的一个目录项中的列从宏观上分为三列文件名文件详细信息文件的物理地址。图8 传统目录项查询文件的时候咱们需要先绝对路径中涉及的每一个目录都从外存加载到内存再在当前目录查询下一级的文件。而将数据从外存导入内存是非常耗时的。图9 传统目录项下匹配文件名流程而文件的查询只需要文件名的匹配。因此传统目录项的问题就是你把很多个很长的目录项导入内存结果你要使用的只有短短的文件名其它的列导进来与否根本不影响。而由于一个磁盘块的空间是有限的在传统目录项的长度下假设一个磁盘块只能存五个目录项一个目录有十个目录项。那咱们匹配一个目录还得导两次磁盘块。如果咱们优化一下把目录项的列变成两个短短的列文件名文件剩余信息存储的物理地址。这样一个目录项的长度大大缩短那么一个磁盘块也能从只能存五个目录项变成能存十个目录项了。那咱们IO的次数就会大大缩短从查询的流程上就能减少很多时间从而优化查询时间。而inode就是存储文件剩余信息的那一整个位置的总称。实际上有了inode传统目录项就会被优化为只有两个列的目录项文件名inode指针。图10 inode目录项下匹配文件名流程inode本质就是保存文件的信息的当你点击某个文件的属性看到了很多文件信息这个时候inode就被加载到了内存才能被你看到inode中存储的信息。当inode被加载到内存后它会在外存inode的基础上多添加几列数据比如打开计数器记录有多少进程打开了这个文件等等不过这些数据只在内存中存有当咱们关闭文件把内存inode存入外存并销毁后外存inode是不会有这几列数据的。这很好理解毕竟有多少进程打开了这个文件就是当前文件的一个信息不记录到inode中记录到哪呢这就是内存inode与外存inode的区别。文件打开表文件打开表分为系统文件打开表和进程打开文件表。一个系统只有一个系统文件打开表。在路径解析全过程中我会稍微解释一下咱们为啥需要一份文件打开表。在这里就简单讲述一下使用过程。进程打开文件表中的表项有三个列索引号读写指针和访问权限。在这里我们先只关心索引号这一列。一个进程对应一个进程打开文件表。上文提到当用户提出要查询某个文件咱们会根据路径进行匹配匹配成功后进入下一步操作。这个下一步操作中不可绕过的就是接下来咱们要说的这一步打开文件。用户决定对文件进行操作是指对文件数据进行操作。所以它需要定位文件的物理位置取出文件数据。于是系统会先打开文件将该文件对应的目录项导入系统文件打开表并返回给用户一个索引号。一个进程可能会打开很多文件当它需要对某个文件进行操作的时候就根据表项中的索引号去系统打开文件表中匹配这样就可以拿到系统打开文件表中的inode进而定位物理地址能够对该文件里头的具体数据进行操作了。图11 打开文件表因此有了打开文件表进程在查询文件的时候不是靠文件名匹配而是靠索引号匹配。这很好理解毕竟一个系统中有很多重名文件如果咱们在整个系统唯一的系统文件表中用文件名标记文件又用文件名搜索文件就会造成搜索一个文件跳出多个文件的情况了。路径解析全过程常用的文件系统是树形结构因此当我们想要打开一个文件的时候需要以路径的形式打开。用户输入路径C:\Users\yufeng\language.txt目录被设计出来的作用就是为了根据文件名找到文件的信息尤其是这个文件在外存的存放位置。因此这一步OS在目录中是这样操作的。C卷是一个逻辑磁盘在最初的定义中一个物理磁盘会被分为多个卷我们姑且分为C、D、E三个卷。图12 逻辑磁盘2. 在C目录中找到Users 文件对应的目录项拿到Users 的 inode 指针。比如这个inode 指针为2意思就是他是inodes这个数组的第二个元素接着我们去inodes这个数组所在的磁盘中拿到inode ,再从这个inode中读取users目录的物理地址就读出了Users目录。接着在Users目录中以同样的方法拿到yufeng目录再从yufeng目录中拿到language.txt文件。图13 获取文件物理地址全过程由于language.txt是第一次被打开所以它的文件属性会被存储到文件打开表中文件打开表会返回索引号给用户。为什么要设置一个文件打开表注意此处需要区分两个概念“打开文件”系统调用open 和 “读写文件”系统调用read/write。open的职责简单说就是定位文件找到用户所需文件在磁盘中的位置。read/write的职责将磁盘中的文件内容读到内存中进行操作比如显示在屏幕中或者修改文件内容然后写回磁盘。假设同一个文件language.txt被进程A和进程B先后打开且进程A尚未关闭该文件。如果没有系统文件打开表或表项不共享进程A对文件进行读操作时为了获得文件的物理地址又需要重新走一趟图14的流程。假设进程A要执行100次读操作那系统就要执行图14的步骤100次也就是通过文件名匹配目录项从磁盘中将目录项和文件信息inode导入内存才能获得物理地址。进程B同上。图14 没有文件打开表获取文件物理地址过程2. 有了系统文件打开表 ​1. 进程A对系统文件进行读操作时就不需要重复图14的方式通过磁盘IO得到物理地址而可以直接在内存中通过索引号得到物理地址。 ​2. 进程B在想要打开该文件时系统会发现诶进程B想要获得的inode之前已经被打开过了诶就在系统文件表里于是进程B在打开文件时也不用通过磁盘IO获取文件物理地址而可以直接在系统文件表中获取索引号并将该文件的inode中的“打开计数器”1表示我这个进程也要打开你这个文件。如图15。图15 有了文件打开表获取文件物理地址过程有了这张表图14中昂贵的磁盘查找和I/O操作只需要执行1次第一次打开时。后续的99次重复“打开”操作都不再需要完整路径解析而是直接走图15中的极速内存查找通道——只需要输入索引号在系统文件打开表的内存数组里一查物理地址就拿到了。这就完美节省了99次目录查找时间和99次读取目录项/inode的磁盘I/O时间。用户对language.txt进行读写操作。用户进行读操作。首先通过进程文件打开表的读写指针它本质就是该文件起始地址之后的偏移量。我们就可以定位确切的物理地址。接着将我们要读的部分从磁盘中读入内存用户就可以看到要读的内容啦。用户进行写操作。首先通过进程文件打开表的读写指针定位了咱们用户写入的具体位置接着往磁盘的这个具体位置将用户写入的数据加上后面的原数据全部直接覆盖写到磁盘里面。实际经过缓存最终持久化用户真正的关闭文件。这个时候系统文件打开表中的打开计数器会减1。当打开计数器0时该文件对应的系统文件打开表的表项会被删除。

相关新闻

2026/9/5 9:25:25

毕业设计之高校宿舍管理系统

题目:高校宿舍管理系统一、项目介绍随着信息化时代的到来,管理系统都趋向于智能化、系统化,高校宿舍管理系统也不例外,但目前国内的市场仍都使用人工管理,市场规模越来越大,同时信息量也越来越庞大&#xf…

2026/9/5 9:25:25

FDE架构师常用网站及工具

FDE架构师常用的工具: 文件传输:WinSCP 截图软件:Snipaste SSH连接工具:XShell Markdown查看工具:Typora 翻译软件:谷歌翻译 Agent:GoogleAgent 浏览器:Google 本地播放器&#xff1…

2026/9/5 9:25:25

gpui可能确定要步flutter 后尘了

如题所述,gpui真的可能要步flutter 的后尘,代码库要分叉出来了. 事件起因gupi生态中,个人认为最重要的组件库gpui-commpent创建者昨天喊话zed,gpui已经有300多天没有发布最新的crates版本了. 我们都知道.如果不使用crates版本.只能直接使用github的链接引用方式.gpui-componemn…

2026/9/5 10:20:32

AI开发者工作负荷管理:从Jason Liu暂停更新看可持续开发策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/5 10:20:32

Google Pics深度解读:提示词驱动图像创作的办公革命

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/5 10:20:32

注塑产品气泡的原因分析与解决方案15

1. 引言在注塑成型过程中,产品表面或内部出现气泡是常见的质量缺陷之一。气泡不仅影响产品的外观,还会降低其力学性能和密封性,严重时甚至导致产品报废。本文将从气泡的形成机理出发,系统分析产生气泡的主要原因,并给出…

2026/9/5 10:15:32

谷歌为 Gmail、Docs 和 Keep 推 AI 语音助手,移动操作更便捷!

谷歌为办公应用注入 AI 语音交互新活力谷歌正在为 Gmail、Docs 和 Keep 推出由人工智能驱动的语音助手模式——Gmail Live、Docs Live 和 Keep Live,让用户能通过语音与这些应用交互并实现管理操作。这一功能与谷歌聊天机器人的 Gemini Live 体验类似,方…

2026/9/5 2:46:54

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/5 2:46:52

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/5 2:44:34

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/5 0:04:47

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流

流式背压机制:避免前端渲染卡死与内存暴涨的滑动窗口限流在大模型流式输出(Streaming)与智能体实时推流的架构中,生产环境中经常出现一种“上下游生产消费速率严重失衡”的极端情况: 生产端极速产出:大模型…

2026/9/5 2:45:13

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/5 2:30:42

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/5 2:46:50

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…