考研408——操作系统
操作系统
操作系统的分类
- 单道批处理系统
- 多道批处理系统(IO阻塞就切换到下一道程序)
- 分时操作系统(时间片轮转)
- 实时操作系统(及时性、可靠性)
- 网络操作系统(分布式操作系统)
进程同步
同步互斥机制的设计原则
- 空闲让进
- 忙则等待
- 有限等待
- 让权等待
死锁
死锁产生的必要条件
- 互斥条件
- 请求和保持条件
- 不可剥夺条件
- 循环等待条件
内存管理策略
页缓冲队列
为什么要有页缓冲队列呢? 请看下面的场景
场景一
在频繁抖动的情况下,假如说进程的某个页面刚被换出到磁盘,又被访问了,那这时候进程仍然需要将该页面重新从磁盘中读到内存中再读取。此时这短时间内的一来一回,就会大大增加时间开销,降低运行效率
引入页缓冲队列之后,内存中的某个页面在从内存中换出时,并不会直接写回磁盘,而是会先头插入页缓冲队列中(页缓冲队列位于内存中),等当前页面移动至队列尾部(或者到指定时间)时,才会将它写回内存。
场景二
假如说操作系统在一分钟内一共换出了10页,那么正常情况下我们应该在这1分钟内进行十次IO。但是我们知道,内外存之间的IO时间是很长的,如此频繁地进行IO,所需的时间开销极大,相应地,计算机的运行效率降低的也比较厉害。
那为了减少IO开销,我们就引入了页缓冲队列,让这1分钟内换出的页面并不立即写回内存,而是先存入页缓冲队列中,1分钟结束时,我们再将这一分钟内页缓冲队列中的所有页面一次性写回磁盘。这样做,那么1分钟就只需要进行一次IO,大大减小了IO的时间开销,提高了运行效率(本质上还是以空间换时间的做法)
工作集和驻留集
工作集的概念:工作集指的是在某段时间间隔内,进程要访问的页面集合。一般来说,工作集W可由时间t和工作集窗口尺寸Δ来确定。

假设工作集窗口尺寸Δ设置为5,则在t₁时刻,进程的工作集为{2,3,5},t₂时刻,进程的工作集为{1,2,3,4}。
实际应用中,工作集窗口会设置得很大,对于局部性好的程序,工作集大小一般会比工作集窗口Δ小很多。
驻留集的概念: 所谓的驻留集,就是操作系统分配给进程的物理块的数量
驻留集和工作集的大小关系:
工作集反映了进程在接下来的一段时间内很有可能会频繁访问的页面集合,因此驻留集大小应该大于等于工作集的大小,否则进程在运行过程中会频繁缺页。
软链接与硬链接
硬链接
创建硬链接时,系统只需在指定目录下新增一个目录项,让该目录项指向目标文件的 inode,并将 inode 的链接计数(硬链接数量)加 1,这就完成了一个文件的硬链接的创建
创建出的硬链接,除了名字和完整路径可以和源文件不同,其余所有属性都和源文件相同,并且会实时同步(本质就是他们指向同一个innode)
软链接
软链接就是创建一个新文件,把目标文件的完整路径作为这个文件的内容
打开文件表
进程文件打开表
就是你记的那个fd_array,每个进程都独立私有一张进程打开文件表。表项的内容主要是进程的私有数据:
- 指向系统文件打开表对应表项的指针
- 进程对这个文件的读写指针
- 进程对这个文件的访问权限
系统打开文件表
除了进程打开文件表中的那些私有属性之外,其他的全局属性都会记录在系统打开文件表中,每个打开的文件通过innode号作为它的唯一标识
文件的逻辑结构
什么是文件的逻辑结构?
啥叫文件的逻辑结构呢?其实很好理解,就是在用户的视角下,看到的文件结构。啥叫用户视角下看到的文件结构?难道用户看到的文件结构不是真正的文件结构吗?没错,确实不是,那为啥呢?
这主要是因为,为了实现高效的存储(为了在有限的空间内装入尽可能多的文件),工程师们肯定会设计一些特殊机制。而这些特殊机制在用户层面是不好理解的,如果把原原本本的结构展现给用户,这样就会增加用户的理解成本,不利于产品的推广。为了使产品的用户尽可能的多,我们肯定是要想尽一切办法,让用户使用计算机变得很简单。文件结构也是一样,我们要对实际的文件结构进行封装,使得用户看到的文件结构是容易理解的。
文件的逻辑结构有哪些?
按逻辑结构,文件可划分为无结构文件和有结构文件两大类。
1. 无结构文件
无结构文件是最简单的文件组织形式,它是由字符流构成的文件,所以又称流式文件,其长度以字节为单位。对流式文件的访问,通过读/写指针来指出下一个要访问的字节。在系统中运行的大量源程序、可执行文件、库函数等,这些文件就是无结构文件。由于无结构文件没有结构,因而对其中具体内容的访问只能通过穷举搜索的方式,因此这种文件形式对很多应用不适用。
2. 有结构文件
按照记录是否等长分类
有结构文件是指由一个以上的记录构成的文件,所以又称记录式文件。各记录由相同或不同数目的数据项组成,根据有结构文件中各记录的长度是否相等,可分为定长记录和变长记录两种类型
- (1)定长记录:文件中所有记录的长度都是相同的,各数据项都在记录中的相同位置,具有相同的长度。检索记录的速度快,方便用户对文件进行处理,广泛用于数据处理中。
- (2)变长记录:文件中各记录的长度不一定相同,原因可能是记录中所包含的数据项数目不同,也可能是数据项本身的长度不定。检索记录只能顺序查找,速度慢。
按照记录的组织形式分类
有结构文件按记录的组织形式可以分为顺序文件、索引文件、索引顺序文件。
(1)顺序文件
顾名思义,顺序文件中的记录是一个接一个地顺序排列的,顺序文件的记录既可以是定长记录,也可以是变长记录。顺序文件中记录的排列有两种结构:
- 串结构,各记录之间的顺序与关键字无关,通常是按存入的先后时间进行排列,检索时必须从头开始顺序依次查找,比较费时;
- 顺序结构,所有记录按关键字顺序排列,对于定长记录的顺序文件,检索时可采用折半查找,效率较高。
在对记录进行批量操作,即每次要读或写一大批记录时,顺序文件的效率是所有逻辑文件中最高的。此外,对于顺序存储设备(如磁带),也只有顺序文件才能被存储并能有效地工作。在经常需要查找、修改、增加或删除单个记录的场合,顺序文件的性能较差。
(2)索引文件
为什么要引入索引文件?
有人看到这可能会想,我看你前面说的顺序文件结构,不是蛮好的吗?为啥又要引入这个索引文件呢?引入索引文件,肯定是为了解决顺序文件结构存在的问题。那顺序文件结构存在啥问题呢?最主要的问题就是:变长记录的顺序文件,是不能支持随机访问的
对于定长记录的顺序文件,要查找第iii条记录,可直接根据下式计算得到第iii条记录相对于第1条记录的地址:Ai=i×LA_i = i \times LAi=i×L。然而,对于变长记录的顺序文件,要查找第iii条记录,必须顺序地查找前i−1i-1i−1条记录,以获得相应记录的长度LLL,进而按下式计算出第iii条记录的地址:
Ai=∑i=0i−1Li+1A_i = \sum_{i=0}^{i-1} L_i + 1Ai=∑i=0i−1Li+1
索引文件是如何解决上述问题的?
前面我们说到,变长记录的顺序文件只能顺序查找,效率很低。为了提高变长记录文件的查找效率,工程师们就设计出了索引文件结构。
所谓的索引文件结构,其实就是在顺序文件结构的基础上,多建立一张索引表(如下图所示)

虽然文件中各个记录的长度不相同,但是它们在索引表中表项的长度都是相同的。引入索引表之后,我们想要找到某个文件,就再也不用从头挨个找,而是只需要在索引表中进行小范围查找即可。又因为索引表中各表项是按照关键字进行有序排列的,因此我们在索引表中也不用顺序查找,直接折半查找即可,找到对应表项之后,按照里面的指针,就能直接找到对应的记录。
到此为止,引入索引表之后,我们再想查文件中的某条记录,就不用对文件进行顺序查找了,而是只需要对索引表进行折半查找。这就大大提高了查找效率
索引表按关键字排序,因此其本身也是一个定长记录的顺序文件。引入索引表之后,就将对变长记录顺序文件的顺序检索,转变成了对定长记录索引文件的随机检索,从而加快了记录的检索速度。
索引文件结构的缺点
索引文件结构最大的缺点就是,它引入的那个索引表是额外占空间的,相当于是牺牲了一些存储空间,换取了更高的查找效率
(3)索引顺序文件
为什么要引入索引顺序文件结构?
看到这里肯定有人还会问,索引文件结构这么好,为啥还要引入索引顺序文件结构呢?同样的,引入索引顺序文件结构的目的,肯定是为了解决索引文件结构中存在的问题。那这个问题是啥呢?是前面提到的索引表额外占空间吗?不是的,前面那个问题太根本了,真没办法优化。但是一些小问题,还是能继续优化的,比如:
如果我们有10000条索引表项,你现在要通过折半查找的方法查找其中的一条,那么查找的时间复杂度就是O(log n),即100次。有的人说100次还是有点多,查起来其实还是蛮费劲的,为了进一步的减少查找次数,我们就引入了索引顺序文件结构。所谓的索引顺序文件结构,其实就是多级索引结构。试想一下,如果我采用了二级索引结构,那么我二级索引目录就只有100个表项,1级索引目录也只有100个表项,那么查找的次数就变成了2∗log100=202 *log 100=202∗log100=20次
索引顺序文件结构的缺点
很明显,多级索引结构在进一步提高查找效率的同时,其牺牲的存储空间也变多了
(4)直接文件、散列文件
将文件中的某些关键字(比如innode编号)与文件的物理地址建立映射,生成对应的哈希函数。此后我们就能通过hash(innode)直接得到文件对应的物理地址了
总结
到这里大家其实已经能看出来了,所谓的文件逻辑结构,其实是和查找算法息息相关的。设计多种多样的文件逻辑结构,本质上就是为了方便文件中记录的查找
文件的物理结构
什么是文件的物理结构?
这个应该比较好理解,就是文件具体在存储器中是咋存的。
文件的物理结构有哪些?
(1)连续分配
这个分配规则是我们很容易想到的,假如说一个文件的大小占两个磁盘块,那我在实际存放的时候,就把这个文件放在磁盘的两个连续磁盘块中。即:逻辑上相邻的数据,在实际存储时也是相邻的
放好之后,在磁盘的目录块中记录这个文件的起始磁盘块号,以及这个文件总共占几个磁盘块。
连续分配的优点:
- 直观,容易理解,由于有目录块,可以支持随机访问
连续分配的缺点:
- 容易产生很多无法利用的外部小碎片
- 无法满足文件动态增长的需求
- 删除一个文件时,需要整体移动后面的所有文件
(2)链接分配——隐式链接方案
为什么要引入链接分配?
肯定还是因为前面的连续分配存在一些问题。
- 外部碎片问题会导致磁盘的空间利用率变低,文件不支持动态增长则没办法满足现在大部分文件的需求。这俩都是很要命的问题
- 为了解决这俩问题,工程师们才设计出了链接分配的方案
隐式链接分配方案如何解决上述问题?
假如说一个文件的大小是3个磁盘块,在连接分配方案中,我们不要求逻辑上相邻的磁盘块在物理上也必须相邻,而仅仅要求:每个磁盘块的最后一小块位置不存数据,而是存一个指针,指向与其逻辑上相邻的下一个磁盘块。这样虽然这些磁盘块物理上不相邻,但我们却可以通过指针将它们串起来。
在磁盘的目录块中,如果采用隐式链接分配方案,那文件的目录项中只需要记录这个文件第一个磁盘块和最后一个磁盘块的指针
采用隐式链接分配方案,逻辑上相邻的磁盘块物理上可以不再相邻,从而使得我们可以见缝插针地为文件拓展磁盘块,上面的两个问题就都解决了
隐式链接分配方案的缺点
-
隐式链接分配方案解决了外部碎片和文件动态增长的问题,付出的最大代价就是:文件不再支持随机访问了,只能顺序访问
-
还有就是,多出的指针要占用额外的存储空间,使得存储器的空间利用率降低了
-
假如说一个文件占用10个磁盘块,也就是说有9个指针,只要这9个指针中的任意一个坏了,这个文件就不完整了,就没法用了
(2)链接分配——显示链接方案
为什么要引入显示链接方案
前面我们说的隐式链接方案中,是在各磁盘块的尾部加一个指针。显示链接方案其实就是将原本分散在各磁盘中的指针都集中起来,形成一个FAT表。有人这时候可能就会问,你这么做有啥好处呢?请看下面的场景:
这样假如说一个文件占用10个磁盘块,我就想将第五个磁盘块读入内存。如果采用隐式链接分配方案,那肯定是要先读第一个磁盘块,拿着第一个磁盘块最后的指针去读下一个磁盘块,这样依次读到第五个磁盘块。而如果我们用的是显示链接方案,我们就可以直接在FAT表中找到第五个磁盘块的指针,然后一次性就把第五个磁盘块读进来。
下图就是FAT表的简单示例,其中-1表示是文件的最后一个磁盘块,-2表示空闲的磁盘块

显示链接方案的优缺点
显示链接支持文件动态增长,也可以防止外部碎片的出现,还能支持随机访问(访问速度也更快了)。但代价就是:额外需要占用的存储空间更多了(FAT表在操作系统启动时就会被读入内存,从此以后常驻内存)
(3)索引分配——单级索引分配
到这里肯定有人还会问,显示链接方案这么好,为什么要引入索引分配呢?那肯定是显示链接方案也存在着它的问题。那是啥问题呢?
刚刚我们才说过:在显示链接方案中,FAT表需要常驻内存。无论我们是读一个文件,还是100个文件,都是这样。那对于读一个文件的情况,你把整个FAT表都读进来,显然有些杀鸡用牛刀了。这时候我们就在想,如果我们读哪个文件,只把这个文件相关的磁盘块信息读进来,那不就好了吗?索引分配就是这个思路!
索引分配策略会为每一个文件都分配一个专属的目录块,在其中创建一个小的文件目录,将属于这个文件的所有磁盘块号(磁盘块索引)都记录到这个文件目录中(具体例子见下图)

这样设计之后,在只读一个文件的场景下,采用索引分配,就比采用显示链接分配要好,因为索引分配只需要读取这个文件的目录块,而显示连接分配则需要读入整个FAT表
但是这样设计也是有缺点的,比如一个文件只占俩磁盘块,那你目录块中其实只需要存俩磁盘块号,但是目录块实际上可以存几百个磁盘块号,这就造成了空间的极大浪费
(3)索引分配——多级索引分配
为什么要引入多级索引分配?肯定是前面单级索引分配有不方便的地方。那哪里不方便呢?请看下面的场景
假如说我一个文件占用了10000个磁盘块,而我一个目录块里面最多存100个磁盘块号,那我就需要用100磁盘块来作为目录块。现在我想查找该文件目录中某个目录项,按折半查找来算,平均查找次数就是100次。而我要是采用二级目录的话,平均查找次数就是缩减到20次(这个我们前面在索引顺序文件中提到过,这里不再赘述)

当然这个多级索引也有缺点,比如说我想找某个文件第一个磁盘块的磁盘块号。如果用单级索引,只需要访问一次目录块,而用二级索引,就要访问两次。即对于某些比较靠前的磁盘块,多级索引会增加它们索引时访问磁盘的次数
(3)索引分配——混合索引分配
在多级索引分配的基础上,为了减少逻辑上靠前的磁盘块的索引时间,我们就引入了混合索引分配。典型的混合索引分配举例大家都很熟悉,就是我们linux系统的innode结构(这个你肯定很熟悉了,我就不多说了)

文件系统的结构是怎样的?
文件系统结构的本质就是用分层抽象屏蔽硬件差异,用元数据+数据分离实现高效管理:
一、文件系统的核心分层结构
从底层硬件到上层用户接口,文件系统通常分为5层,每层负责特定功能,且上层依赖下层提供的服务。这种分层设计让文件系统的维护、扩展和跨硬件适配更灵活。
| 层级 | 核心功能 | 示例(以Linux Ext4为例) |
|---|---|---|
| 1. 硬件层 | 存储文件的物理介质,提供“块级”数据读写能力(最小单位为扇区,通常512B) | 机械硬盘(HDD)、固态硬盘(SSD)、U盘的物理扇区 |
| 2. 驱动层 | 操作系统与硬件的接口,将硬件的“扇区”抽象为“逻辑块”(大小通常4KB/8KB) | 硬盘驱动程序(如sd系列驱动),屏蔽硬件差异 |
| 3. 核心层 | 文件系统的核心逻辑,管理“逻辑块”的分配、回收,以及文件的元数据和数据存储 | Ext4的super block(超级块)、inode(索引节点) |
| 4. 接口层 | 提供用户/应用程序访问文件的“系统调用接口”,屏蔽底层实现细节 | Linux的open()/read()/write(),Windows的CreateFile() |
| 5. 应用层 | 用户直接交互的工具或程序,基于接口层实现文件的可视化管理或操作 | Windows资源管理器、Linux的ls/cp命令、macOS访达 |
二、文件系统的核心数据结构(以Ext4为例)
文件系统的核心设计思路就是通过元数据(描述文件的信息) 和数据(文件的实际内容) 分离存储,实现高效的文件管理。下图是一个文件系统可能的结构布局

0. 引导块
C盘中引导块里面存了一段引导程序,运行这段引导程序,就能启动存在C盘中的操作系统
1. 超级块(Super Block):文件系统的“总目录”
超级块里面存的是啥呢?超级块里面记录的是一份文件系统的具体配置信息,具体来说:
- 超级块所属的这个文件系统的类型
- 这个文件系统有多少个逻辑块,每个逻辑块的大小,用了多少个逻辑块,还有多少个空闲的逻辑块
- 规定这个文件系统最多允许创建多少个innode,每个Innode的大小,现在创建了多少个innode,还能创建多少个innode
- 规定一个文件的文件名最多有多长,一个文件最多有多大等等
- 记录这个文件系统的创建时间、最近的挂载时间
2. 索引节点(inode):文件的“身份证”
在文件系统内,每个文件/目录都对应一个唯一的inode(索引节点),它存储文件的“元数据”(描述信息),但不包含文件名和实际数据。
3. 数据块(Data Block):文件的“内容仓库”
数据块用来存储文件的具体内容,是同时也是存储的最小单位(大小由文件系统预设,如4KB),每个数据块对应一个逻辑块号。
4. 空闲块管理:避免空间浪费
文件系统需要实时跟踪“空闲的逻辑块”和“空闲的inode”,避免分配时重复或遗漏。常见的管理方式有3种:
- 位图(Bitmap):用1位(bit)表示1个块/inode的状态(0=空闲,1=已用),查询和修改效率高(Ext4、NTFS均采用);
- 链表(Linked List):将空闲块/inode用链表串联,分配时从表头取,回收时插入表尾(适合小文件系统,效率低);
- 组块(Group):将文件系统划分为多个“块组”(如Ext4的Block Group),每个块组内包含独立的超级块、inode表、数据块,分散负载(提升大文件系统的性能)。
主流的文件系统有哪些?
主要就是 Linux的Ext4,Windows的NTFS,还有macOS的APFS。
不同文件系统适配的硬件也不一样,比如SSD适合Ext4/APFS,机械硬盘适合NTFS
| 对比维度 | Linux(Ext4) | Windows(NTFS) | macOS(APFS) |
|---|---|---|---|
| 根目录 | /(唯一根目录,所有分区挂载到子目录) | 多根目录(如C:\、D:\,对应不同分区) | /(唯一根目录,分区通过“宗卷”管理) |
| 特殊目录 | /home(用户目录)、/etc(配置文件) | C:\Users(用户目录)、C:\Windows(系统文件) | /Users(用户目录)、/System(系统文件) |
| 元数据扩展 | 支持xattr(扩展属性,如文件标签) | 支持“访问控制列表(ACL)”“加密(EFS)” | 支持“快照”“加密”“文件克隆” |
| inode特点 | inode号全局唯一 | 用“MFT(主文件表)”替代inode,MFT项存储元数据 | 用“inode”,但支持动态调整inode大小 |
开机后操作系统的启动流程
- 通电后先找到BIOS:计算机开机后,首先会找到固化在主板芯片上的BIOS(Basic Input/Output System,基本输入输出系统),将它放到处理机上运行
- BIOS 完成硬件自检与硬件初始化: BIOS 首先检查硬件(内存、硬盘等),然后读取磁盘第 0 扇区的 MBR;
- MBR 定位C盘:MBR 中的引导程序扫描分区表,找到标记为 “活动” 的分区(通常是 C 盘);
- 加载分区引导块:MBR 将控制权交给活动分区的第一个扇区(即 C 盘的分区引导块),BIOS 读取该引导块;
- 分区引导块加载系统核心:C 盘的分区引导块执行代码,识别 NTFS 文件系统,找到并加载bootmgr;
- 启动操作系统:bootmgr加载BCD配置,调用winload.exe加载 Windows 内核,最终完成系统启动。
BIOS存在计算机的什么位置?负责什么工作?
BIOS的全称是 Basic Input/Output System(基本输入输出系统),本质是一段固化在主板ROM芯片(早期为只读ROM,现代为可擦写的Flash ROM)中的底层程序。
BIOS是计算机通电后运行的第一个程序,负责在操作系统(Windows/macOS/Linux)启动前,完成“硬件自检”和“硬件初始化”,搭建起“硬件与操作系统”的沟通桥梁
BIOS的所有操作都发生在“操作系统启动前”(即我们常说的“开机自检阶段”),核心功能可归纳为3点:
1. 硬件自检(POST:Power-On Self-Test)—— 硬件“健康检查”
这是BIOS通电后的第一个任务:自动检测主板、CPU、内存、硬盘、显卡、键盘等核心硬件是否正常。
2. 硬件初始化—— 让硬件“进入工作状态”
自检通过后,BIOS会对硬件进行“初始化配置”:比如设定CPU的基础频率、内存的时序、硬盘的识别模式(IDE/ACHI)、显卡的输出分辨率等,确保所有硬件都处于“可被调用”的状态。
3. 加载并执行引导程序—— 找到“操作系统入口”
执行MBR中的主引导程序(Boot Loader),启动操作系统
MBR里面装的是啥?
MBR(Master Boot Record,主引导记录)是硬盘的“第一个扇区”(位于硬盘0磁道0柱面1扇区,大小为固定是512字节),是BIOS启动时识别硬盘、引导操作系统的关键数据区域。
MBR的512字节并非“杂乱存储数据”,而是按“引导程序+分区表+校验位”的结构划分,每个部分的功能不可替代
主引导程序的作用
BIOS硬件自检与初始化完成后,会将MBR中的这部分程序加载到内存),主引导程序执行时,首先会检查硬盘分区表的有效性。如果检查没问题,那就会紧接着从分区表中找到“活动分区”(基本上就是C盘),并将该分区的“分区引导记录(PBR)”加载到内存,最终由PBR启动操作系统。
硬件分区表的作用
记录硬盘的分区配置信息
- 你这个硬盘有几个分区
- 每个分区大小多少,起始位置在哪,每个分区采用的文件系统是什么,是不是活动分区等等
校验位
MBR的标识位,用来表示MBR的结束
什么是文件系统挂载?
假如说你现在手里面有一个U盘,在你将U盘插进电脑之前,你电脑肯定是没办法访问U盘上的文件的。那现在你将U盘插入电脑,你就会发现在电脑的文件目录中多了一个U盘,然后你点进U盘目录之后,就可以访问里面的文件了

在上面的例子中,你之所以能看到U盘的文件目录,害得多亏文件系统挂载。如果没有这一步,U盘对你来说就是个黑盒,你的计算机只能检测到多了一个外设,并不能看到里面有啥东西。
而进行文件系统挂载之后,操作系统会将U盘中文件的目录结构挂载到计算机的根目录下,这样我们通过文件目录,就能看到U盘中有哪些文件了,这个U盘就从一个黑盒变成了一个白盒。里面的东西对我们既可见,又可访问
什么是虚拟文件系统(VFS)?
VFS是操作系统“模块化、可扩展”设计的经典案例
VFS的功能不是管理文件数据,而是通过抽象统一的接口,将“上层操作”与“底层实现”解耦,让应用无需关心文件存储在何处、用何种格式存储,同时让新文件系统的接入更简单。没有VFS,现代操作系统无法高效支持多设备、多协议的文件访问需求。
前面我们都已经介绍过很多文件系统了,感觉它们用起来应该挺好的呀,为什么还要提出虚拟文件系统(VFS)的概念呢?
前面介绍的文件系统的功能,是用来管理文件。而虚拟文件系统不是用来管理文件的,而是用来统一文件系统与用户层交互的借口的。
之所以要引入VFS,肯定是在引入之前存在一些问题,为了解决这个问题,才引入的VFS。那引入VFS到底是用来解决什么问题呢?
在没有引入VFS之前,操作系统会面临的最大问题就是:
应用程序与文件系统强耦合:不同文件系统的访问接口在设计时肯定是有一些区别的。假如说我应用层想访问EXT4文件系统中的文件,就需要调EXT4文件系统的访问接口,应用层想访问NTFS文件系统中的文件,就需要调用NTFS文件系统中的访问接口。
这对于用户来说是很不方便的,用户肯定是希望把底层的硬件差异给封装起来,不管你底层用的是啥硬件,用的是啥文件系统,我就通过你外部暴露出来的这个统一的接口就能访问。VFS虚拟文件系统干的就是这个事情
引入VFS之后,上层应用/内核模块只需调用VFS提供的标准文件操作接口,无需关心底层是本地硬盘、U盘还是网络存储。
当新增一个文件系统时,只需让这个文件系统在适配层额外实现一下VFS定义的“接口规范”就行。
引入VFS之后,读取文件的工作流程
以Linux系统中应用调用read()读取文件为例,VFS的交互流程可拆解为6步,清晰体现其“抽象层”角色:
- 应用发起请求:应用调用标准C库函数
read(fd, buf, size),其中fd是“文件描述符”(指向进程的文件对象)。 - 内核接收请求:内核将
read()映射为系统调用sys_read(),并通过fd找到对应的VFS文件对象。 - 获取inode与超级块:从文件对象中找到对应的VFS索引节点(inode) ,再从inode中找到对应的VFS超级块(Superblock) ——此时已确定文件所属的“具体文件系统”(如EXT4)。
- 调用文件系统适配层:VFS通过超级块中的“文件系统操作函数表”(如EXT4的
ext4_file_operations),将read请求转发给具体文件系统的实现函数(如ext4_file_read)。 - 底层文件系统交互:具体文件系统(如EXT4)根据inode中的元数据(如数据块位置),与硬件驱动(如硬盘驱动)交互,从物理设备/网络中读取原始数据。
- 返回结果:底层数据经具体文件系统、VFS逐层向上传递,最终通过
read()函数返回给应用,存入buf缓冲区。
文件系统与硬盘的对应关系
文件系统与硬盘的三种常见对应关系
1. 最常见:一个文件系统对应“一个硬盘的一个分区”
这是日常使用中最普遍的场景,即先将一块物理硬盘划分为多个分区(如Windows的C盘、D盘,Linux的/分区、/home分区),再为每个分区单独格式化一种文件系统。
2. 跨硬盘:一个文件系统对应“多块硬盘的组合空间”
在需要“扩大存储容量”或“提升可靠性”的场景中,会将多块物理硬盘通过技术手段组合成一个“逻辑存储池”,再在这个“池”上创建一个文件系统——此时一个文件系统对应多块物理硬盘。
常见实现技术包括:
- RAID阵列
- LVM(逻辑卷管理)
技术实现的核心思路就是通过RAID/LVM等技术“打破硬盘物理边界”,将多块硬盘的空间整合为一个“逻辑存储单元”,文件系统作用于这个“单元”,而非单块硬盘。
3. 无硬盘:一个文件系统不对应任何物理硬盘
文件系统的核心是“数据管理规则”,只要能提供“数据读写接口”,数据可以存于内存、网络或远程设备,无需绑定本地物理硬盘。
- 内存虚拟文件系统:如Linux的
/tmp(临时文件系统,数据存内存,重启后消失)、/proc(系统信息文件系统,数据动态来自内核,不占硬盘空间)。 - 网络文件系统:如NFS(网络文件系统)、SMB(Windows文件共享)——这些文件系统的“数据存储在远程服务器的硬盘上”,本地操作系统通过网络访问时,会将其挂载为“本地虚拟文件系统”,但本地不对应任何物理硬盘。
总结:文件系统与硬盘的对应关系规律
| 对应场景 | 文件系统数量 | 物理硬盘数量 | 典型案例 |
|---|---|---|---|
| 单硬盘多分区 | 多个 | 1个 | 1TB SSD分C盘(NTFS)、D盘(NTFS) |
| 多硬盘组合(RAID/LVM) | 1个 | 多个 | 2块1TB硬盘做RAID 0,格式化为EXT4 |
| 虚拟/网络文件系统 | 1个 | 0个(本地) | Linux的/proc、NFS网络共享 |
核心结论:
一个文件系统与硬盘的对应关系是“灵活的”,它既可以“对应一块硬盘的部分空间(分区)”,也可以“跨多块硬盘”,甚至“不依赖本地硬盘”。关键是理解:文件系统是“数据管理规则”,硬盘是“物理存储载体”,规则可以作用于“部分载体”“多个载体”或“非物理载体”,而非一一绑定。
设备独立性软件
什么是设备独立性软件?设备独立性软件是干啥的?
设备独立性软件位于用户层软件与设备驱动程序之间,是操作系统中屏蔽设备硬件差异、让应用程序无需关注具体设备的中间层软件。

设备独立性软件的核心作用
- 向上提供一套统一的操作接口,无论底层是什么硬件设备,应用程序都通过一套标准命令调用设备功能
- 向上屏蔽硬件细节,更换硬件设备时,完成新硬件的适配工作,使得上层的应用程序无需修改应用代码,更换新硬件之后仍能正常运行
- 设备资源管理,具体来说包括:
- 缓冲区管理:用于缓和CPU与I/O设备的速度差异,提高CPU与I/O设备之间的并行性。
- 设备分配与回收:引入逻辑设备名,并实现逻辑设备名到物理设备名的转换;将设备、控制器和通道分配给进程,确保CPU与I/O设备之间能正常通信。
- 虚拟设备:将独占设备改造为共享设备的技术,以SPOOLing(假脱机技术)为例,可以将独占设备改造为共享设备,并将一台物理I/O设备虚拟为多台逻辑I/O设备,从而提高设备的利用率。
设备独立性软件的关键特点
- 与硬件无关,仅依赖设备的逻辑功能描述。
- 设备独立软件引入了逻辑设备和物理设备的概念,程序可以使用逻辑设备名来使用设备
- 是应用程序与设备驱动程序之间的桥梁。
- 包含设备控制逻辑、数据格式转换等核心功能
磁盘调度算法
先来先服务算法(FCFS)
这个没啥好说的,就是按照请求顺序依次访问
最短寻道时间优先算法(SSTF)
请求序列拿到手之后,找出距离当前磁头最近的磁道,然后移动磁头。访问完之后,再从剩下的请求序列中找出距离当前磁头最近的磁道,然后移动磁头,以此循环往复
SCAN算法
这个大家就非常熟悉了,只说一点需要注意的:
- 在SCAN算法中,磁头必须碰到最外侧或者最内层磁道,之后才能转向
很多同学看了上面这个要求之后就有问题了,比如下面这个图

明明184往后已经没有要访问的磁道了,但是SCAN算法依然是跑到头再转向,然后又返回到184,这段路程难道不是白跑了吗,不如我直接在184访问完直接转向
你说的没错,这段路程确实是白跑了,为了节省时间,科学家就针对这一问题,对SCAN算法进行了优化:程序会自动检索请求序列中编号最大和最小的磁道,让磁头在扫描完这俩磁道后立即转向。优化过之后的算法叫做LOOK算法
C-SCAN算法

在C-SCAN算法中 指针碰到一头,依然会立即转向,但转向之后这一趟并不会扫描,而是快速移动到另一头,在另一头再转向之后,才开始扫描
相应的也有C-LOOK算法,让磁头不需要走到头再转向,而是走到请求序列中编号最大/最小的磁道之后就转向
