cmd).bat文件)单核CPU同一时刻只能执行一个程序,多个程序只能并发地执行。多核CPU同一时刻可以同时执行多个程序,多个程序可以并行地执行。
用户程序请求操作系统服务是通过**访管指令(trap指令、陷入指令)**来实现的。执行系统调用的过程如下🔥:
首先需要传递系统调用的参数
执行陷入(trap)指令,使系统进入内核态
在内核态下执行相应的服务程序
返回用户态
注意:陷入(trap)指令是非特权指令,运行在用户态。返回指令是特权指令,运行在内核态
操作系统是一种系统软件,系统软件包括操作系统、数据库管理系统等。操作系统的程序并不都是在内核态运行,操作系统的所有用户程序都是在用户态执行的。
DOS是一个单任务单用户的操作系统,就是没有界面的操作系统
计算机开机后,操作系统最终被加载到RAM(SDRAM-主存里面),BIOS(也叫固件)也是主存的一部分,BIOS是ROM
内核程序的指令我们称为特权指令,应用程序的指令我们称为非特权指令
CPU有两种状态:内核态和用户态
如何区分CPU此时处于哪种状态呢?
如何实现内核态和用户态的切换呢?
非特权指令是在用户态执行,如:trap指令、跳转指令、压栈指令
特权指令是内核态使用的执行,如:I/O指令、设置中断屏蔽指令、清内存指令、存储保护指令、设置时钟指令
读时钟可以在用户态读,置(写)时钟要在内核态置(写)
一个应用程序运行在用户态,当它想要发出系统调用的时候,执行 陷入指令来发出内中断信号,CPU就会切换为内核态,来执行相应的系统调用指令。
陷入指令是在用户态执行的,执行陷入指令之后立即引发一个内中断,使CPU进入核心态
发出系统调用请求是在用户态,而对系统调用的相应处理在核心态下进行
别名:陷入指令 = trap指令 = 访管指令
进程实体的组成有三部分:PCB、程序段、数据段
PCB是给操作系统用的,程序段、数据段是给进程自己用的
一个进程实体(进程映像)由PCB、程序段、数据段组成。进程是动态的,**进程实体(进程映像)是静态的。**可以把进程实体理解为进程在动态执行过程当中某一时刻的快照,也就是进程实体能够反映进程在某一时刻的状态。
动态性是进程最基本的特征,异步性会导致并发程序执行结果的不确定性
进程是进程实体的运行过程,是系统进行资源分配和调度(操作系统决定让进程上CPU)的一个独立单位
PCB是进程存在的唯一标志
设备分配不需要创建进程

注意:有的时候进程可以直接从运行态转换为 就绪态,比如说操作系统给进程分配的时间片用完了、或者处理机被更重要的进程抢占了的时候。所以等待时间片的进程处于就绪态,等待CPU调度的进程处于就绪态。
注意:不能由阻塞态直接转换为运行态,也不能由就绪态直接转换为阻塞态(因为进入阻塞态是进程主动请求的,必然需要进程在运行时才能发出这种请求)
I/O操作完成之前进程在等待结果,状态为阻塞态,完成后进程等待事件就绪,变为就绪态
一个进程的基本状态可以从其他两种基本状态转变过去,这个基本状态一定是就绪态
不属于进程现场信息的是:进程的就绪,阻塞执行等基本状态
共享存储:互斥的访问共享空间
基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢,限制多,是一种低级通信方式
基于存储区的共享:在内存中画出一块共享存储区,数据的形式、存放位置都由进程控制,而不是操作系统。相比之下,这种共享方式速度更快,是一种高级通信方式。
管道通信:写数据从一边写,读数据从另一边读。数据的传输只能是单向的,要么从左向右,要么从右到左。
半双工通信,某一时间段内只能实现单向的传输。如果要实现双向同时通信,则需要设置两个管道。互斥地访问管道(由操作系统实现)写满时不能再写,读空时不能再读
- 管道通信和共享存储是有区别的,共享存储随便存随便取,但是管道通信是先进先出的,先写进的数据先被读出。只有把前面的数据读了,才能读后面的数据
- 写进程往管道写数据,即使管道没被写满,只要管道没空,读进程就可以从管道读数据
- 管道不空就可读
- 读进程从管道读数据,即使管道没被读空,只要管道没满,写进程就可以往管道写数据
- 管道没满就可写
简单理解:QQ是一个进程,QQ发送文件、视频聊天这就是两个线程

线程管理工作都由应用程序负责(包括线程切换),并不需要操作系统管理干预。线程切换可以在用户态下即可完成,无需操作系统干预,CPU并不需要变态。内核级线程的管理工作由操作系统内核完成。
线程调度、切换等工作都由内核负责,因此内核级线程的切换必然需要在核心态下才能完成。
操作系统会为每个内核级线程建立相应的TCB(Thread Control Block,线程控制块),通过TCB对线程进行管理。“内核级线程"就是"从操作系统内核视角能看到的线程”
优缺点
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点:一个用户进程可能会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。

一对一模型:一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点:一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
多对一模型:多个用户级线程映射到一个内核级线程。且一个进程只被分配一个内核级线程。
优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行
重点:操作系统只"看得见"内核级线程,因此只有
内核级线程才是处理机分配的单位,所以多个线程不可以在多核处理机上并行运行
| 要做什么 | 调度发生在 | 发生频率 | 对进程状态的影响 | |
|---|---|---|---|---|
| 高级调度 (作业调度) | 按照某种规则,从后备队列中选择合适的作业将其调入内存,并为其创建进程 | 外存->内存 (面向作业) | 最低 | 无->创建态->就绪态 |
| 中级调度 (内存调度) | 按照某种规则,从挂起队列中选择合适的进程将其数据调回内存 | 外存->内存 (面向进程) | 中等 | 挂起态->就绪态(阻塞挂起->阻塞态) |
| 低级调度 (进程调度) | 按照某种规则,从就绪队列中选择一个进程为其分配处理机 | 内存->CPU | 最高 | 就绪态->运行态 |
进程在操作系统内核程序临界区中不能进行调度与切换。 (√)
进程处于临界区时不能进行处理机调度。(×)
临界资源:一个时间段内只允许一个进程使用的资源,各进程需要互斥地访问临界资源。临界资源一次只能为一个进程所用。
互斥地进入临界区,互斥地执行访问临界资源的代码决定一个程序能否占用处理机执行,是由进程调度决定,而不是作业调度决定。
当进程调度的因素发生了,但是不能进行进程的调度与切换的情况有以下几种:
在系统中进程优先权的设置基本原则:
需要进行进程调度与切换的情况:
不能进行进程调度与切换的情况:
进程调度、切换是有代价的,所以不能说进程切换越频繁,系统的并发度越高
CPU利用率: 指CPU “忙碌”的时间占总时间的比例。 利用率 = (忙碌的时间)/(总时间)
利用率
=
忙碌的时间
总时间
利用率 = \frac{忙碌的时间}{总时间}
利用率=总时间忙碌的时间
系统吞吐量:单位时间内完成作业的数量。系统吞吐量 = (总共完成了多少道作业)/总共花了多少时间
系统吞吐量
=
总共完成了多少道作业
总共花了多少时间
系统吞吐量 = \frac{总共完成了多少道作业}{总共花了多少时间}
系统吞吐量=总共花了多少时间总共完成了多少道作业
周转时间 = 作业完成时间 - 作业提交时间。
周转时间 = 等待时间 + 要求服务的时间(执行时间)。
平均周转时间 = (各作业周转时间之和)/作业数。
等待时间:指进程/作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低。
对于进程来说,等待时间就是指进程建立后等待被服务的时间之和,在等待I/O完成的期间其实进程也是在被服务的,所以不计入等待时间。
对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待被调度的时间。
所以作业的等待时间和进程的等待时间是不相同的
响应时间:指从用户提交请求到首次产生响应所用的时间。

| 算法 | 思想 | 可否抢占 | 优点 | 缺点 | 考虑到等待时间&运行时间 | 饥饿 |
|---|---|---|---|---|---|---|
| FCFS | 非抢占式 | 公平,实现简单 | 对短作业不利 | 等待时间√ 运行时间× | 不会 | |
| SJF/SPF短作业优先 | 默认为非抢占式,也有SJF的抢占式,版本最短剩余时间优先算法(SRTN) | “最短的”,平均等待时间/周转时间最短 | 对长作业不利,可能导致饥饿,难以做到真正的短作业优先 | 等待时间× 运行时间√ | 会 | |
| HRRN高响应比优先 | 非抢占式 | 上述两种算法的权衡折中,综合考虑的等待时间和运行时间 | 满足短作业优先 | 等待时间√ 运行时间√ | 不会 | |
| 时间片轮转 | 抢占式 | 公平,适用于分时系统 | 频繁切换有开销,不区分优先级 | 不会 | ||
| 优先级调度 | 有抢占式和非抢占式 | 区分优先级,适用于实时系统 | 可能导致饥饿 | 会 | ||
| 多级反馈队列 | 抢占式 | 优秀 | 无缺点(可能导致饥饿) | 会 |
先来先服务调度算法FCFS:是先到达的先得到服务。
短作业优先SJF(非抢占式):每次调度时选择当前已到达且运行时间最短的作业/进程。
高响应比优先HRRN:计算所有就绪进程的响应比,选择响应比最高的进程上处理机。
响应比
=
等待时间
+
要求服务时间
要求服务时间
响应比 = \frac{等待时间+要求服务时间}{要求服务时间}
响应比=要求服务时间等待时间+要求服务时间
时间片轮转调度算法:轮流让就绪队列中的进程依次执行一个时间片(每次选择的都是排在就绪队列队头的进程)
优先级调度算法:每次调度时选择当前已到达且优先级最高的进程
只要是短作业优先算法都会产生饥饿。
时间片轮转算法不区分优先级
实时调度算法是根据进程的紧迫程度为其设定执行的优先级,与作业本身的执行时间无关
高响应比优先调度算法既有利于短作业,又兼顾长作业,还实现了先来先服务(作业等待时间越长,响应比就越高)
分时系统中常采用时间片轮转来处理进程调度。
进程调度主要负责选一个进程上CPU
时间片轮转和多级反馈队列一定是抢占式的
| 共同点 | 区别 | |
|---|---|---|
| 死锁 | 死锁一定是"循环等待对方手里的资源"导致的,因此如果有死锁现象,那至少有两个或两个以上的进程同时发生死锁。另外,发生死锁的进程一定处于阻塞态。 | |
| 饥饿 | 都是进程无法顺利向前推进的现象 (故意设计的死循环除外) | **可能只有一个进程发生饥饿。**发生饥饿的进程既可能是阻塞态(如长期得不到需要的I/O设备),也可能是就绪态(长期得不到处理机) |
| 死循环 | 可能只有一个进程发生死循环,死循环的进程可以上处理机运行(可以是运行态),只不过无法像期待的那样顺利推进。死锁和饥饿问题是由于操作系统分配资源的策略不合理导致的,而死循环是由代码逻辑的错误导致的。死锁和饥饿是管理者(操作系统)的问题,死循环是被管理者的问题。 |
- 死锁和饥饿肯定不可能是运行态,而死循环是可以是运行态的
- 死循环一般是由程序员导致的
- 循环等待 => 死锁(发生死锁时一定有循环等待,但是发生循环等待时未必死锁)
死锁预防:破坏四个必要条件,不允许死锁发生
SPOOLing 技术
死锁避免:银行家算法,不允许死锁发生
死锁的检测和解除:资源分配图,允许死锁发生
解除死锁的主要方法有:
同步和互斥:
进程互斥的软件实现方法:
进程互斥的硬件实现方法:
整型信号量存在的问题:不满足让权等待原则
记录型信号量:
互斥信号量初值一般为1,同步信号量的初始值要看对应资源的初始值是多少
结论:互斥信号量的初值为1:代表同一时间只有一个进程进入互斥段
管程中的是条件变量,信号量是数值,代表资源有多少;而条件变量是 true、false,可以理解为0个资源
wait() = P操作,对条件变量P一下,则该进程阻塞,并将其挂在对应的阻塞队列上signal() = V操作,对条件变量V一下,则唤醒对应阻塞队列上的队首进程P操作前,进程变为阻塞态,则信号量的值 ≤ 0;P操作后,进程变为阻塞态,则信号量的值 < 0
当信号量为负时,信号量的绝对值表示等待该资源的进程数量
mutex = -1, 表示此时有1个进程在等待该资源
将高级语言程序编译为若干个目标模块,然后将若干个目标模块链接成一个装入模块,链接后形成逻辑地址,之后再将装入模块再装入到内存运行,装入后形成物理地址。
链接步骤有三种方式:
将指令中的逻辑地址转换为正确的物理地址:
逻辑地址到物理地址的转换,也就是装入,也叫做地址重定位
操作系统需要提供内存保护功能。保证各进程在各自存储空间内运行,互不干扰
内存保护可采取两种方法:
动态重定位是在作业的 执行过程 中装入的
固定分区方式可以使用静态重定位:提前把物理地址算好
对重定位存储管理方式,应为整个系统设置一个重定位寄存器
固定分区管理中分区大小是在系统执行过程中 系统初始化 时建立的。
碎片可以通过紧凑技术来解决
分区分配管理中对每个作业分配的是若干地址不连续的内存单元
| 算法 | 算法思想 | 分区排列顺序 | 优点 | 缺点 |
|---|---|---|---|---|
| 首次适应算法 | 从头到尾找合适的分区 | 空闲分区以地址递增次序排列 | 综合看性能最好,算法开销小,回收分区后一般不需要对空闲分区队列重新排序 | |
| 最佳适应算法 | 优先使用更小的分区,以保留更多大分区 | 空闲分区以容量递增次序排列 | 会有更多的大分区被保留下来,更能满足大进程需求 | 会产生很多太小的、难以利用的碎片,算法开销大,回收分区后可能需要对空闲分区队列重新排序 |
| 最坏适应算法 | 优先使用更大的分区,以防止产生太小的不可用的碎片 | 空闲分区以容量递减次序排列 | 可以减少难以利用的小碎片 | 大分区容易被用完,不利于大进程。算法开销大 |
| 邻近适应算法 | 由首次适应算法演变而来,每次从上次查找结束位置开始查找 | 空闲分区以地址递增次序排列(可排列成循环链表) | 不用每次都从低地址的小分区开始检索,算法开销小 | 会使高地址的大分区也被用完 |

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TnlTNBEu-1661856919228)(操作系统精简版.assets/6.png)]
一个是将内存空间分区,一个是将进程的逻辑地址空间分区
操作系统以页框为单位为各个进程分配内存空间。进程的每个页面分别放入一个页框中。也就是说,进程的页面与内存的页框有一一对应的关系。各个页面不必连续存放,可以放到不相邻的各个页框中。
注意:进程的最后一个页面可能没有一个页框那么大。也就是说,分页存储有可能产生内部碎片,因此页框不能太大,否则可能产生过大的内部碎片造成浪费
为了能知道进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表。页表通常存在PCB(进程控制块)中。

假设某系统物理内存大小为4GB,页面大小为4KB,则每个页表项至少应该为多少字节?
页表项在逻辑上包含了页号和块号,但是在物理上只需要存放块号,只有块号需要占据存储空间。
若页面大小是2的整数次幂,我们可以把逻辑地址分为==(页号,页面偏移量)==两部分:
一个页面的大小是2K个内存单元,则有K位表示页内偏移量
如果有M位表示页号,则说明在该系统中,一个进程最多允许有2M个页面
只要知道页内偏移量的位数,就可以推出页面大小,从而确定逻辑地址的结构
物理地址的计算更加迅速:根据逻辑地址得到==(页号,页内偏移量),根据页号查询页表(页号,内存块号)==,从而找到页面存放的内存块号,将二进制表示的内存块号和页内偏移量拼接起来,就可以得到最终的物理地址
有些奇葩题目中页面大小有可能不是2的整数次幂,这种情况还是得用最原始的方法计算:
页号
=
逻辑地址
页面长度
(
取除法的整数部分
)
页内偏移量
=
逻辑地址
%
页面长度
(
取除法的余数部分
)
页号=\frac{逻辑地址}{页面长度}(取除法的整数部分) \\[15pt] 页内偏移量 = 逻辑地址 \% 页面长度(取除法的余数部分)
页号=页面长度逻辑地址(取除法的整数部分)页内偏移量=逻辑地址%页面长度(取除法的余数部分)
单级页表存在的问题:
页表必须连续存放,因此当页表很大时,需要占用很多个连续的页框。 采用两级页表
没有必要让整个页表常驻内存,因为进程在一段时间内可能只需要访问某几个特定的页面 采用标志位
两级页表的逻辑结构为==(一级页号,二级页号,页内偏移量)==,一级页号也称页目录表/顶级页表/外层页表。
注意:
若采用多级页表机制,则各级页表的大小不能超过一个页面
两级页表访问内存要进行3次
单级页表访问内存要2次
三级页表访问内存要进行4次
四级页表访问内存要进行5次
结论:没有快表结构,n级页表访问内存次数是n+1次
| 地址变换过程 | 访问一个逻辑地址的访存次数 | |
|---|---|---|
| 基本地址变换机构 | 1. 算页号、页内偏移量 2. 检查页号合法性 3. 查页表、找到页面存放的内存块号 4. 根据内存块号与页内偏移量得到物理地址 5. 访问目标内存单元 | 两次访存 |
| 具有快表的地址变换机构 | 1. 算页号、页内偏移量 2. 检查页号合法性 3. 查快表。若命中,即可知道页面存放的内存快号可直接进行5。若未命中,则进行4 4. 查页表,找到页面存放的内存块号,并且将页表项复制到快表中 5. 根据内存块号与页内偏移量得到物理地址 6. 访问目标内存单元 | 快表命中,只需一次访存 快表未命中,需要两次访存 |
快表TCL和普通高速缓存Cache的区别:
分段系统的逻辑地址结构由段号(段名)和段内地址(段内偏移量)所组成。==(段号,段内地址)==如:

用段表记录各个逻辑段在内存中的存放位置
每个段对应一个段表项(也就是段表中的一行),其中记录了==(段号、段长、该段在内存中的起始位置)==
各个段表项的长度是相同的(也就是说段表中的每行在内存当中占据相同的空间)
内存分配规则:以段为单位进行分配,每个段在内存中占据连续空间,但各段之间可以不相邻。
段页式管理分段:段页式管理的逻辑地址结构由==(段号、页号、页内偏移量)==组成
在段页式存储管理系统中,地址映射表是:每个进程一张段表,每段一张页表(了解概念就行)
| 地址变换过程 | 访问一个逻辑地址的访存次数 | |
|---|---|---|
| 分段地址变换机构 | 1. 由逻辑地址得到段号、段内地址 2. 段号与段表寄存器中的段长度比较,检查是否越界 3. 由段表始址、段号找到对应段表项(第一次访存) 4. 根据段表中记录的段长,检查段内地址是否越界 5. 根据基址与段内偏移量得到物理地址 6. 访问目标内存单元(第二次访存) | 两次访存 |
| 段页地址变换机构 | 1. 由逻辑地址得到段号、页号、页内偏移量 2. 段号与段表寄存器中的段长度比较,检查是否越界 3. 由段表始址、段号找到对应段表项(第一次访存:查段表) 4. 根据段表中记录的页表长度,检查页号是否越界 5. 由段表中的页表地址、页号得到查询页表,找到相应页表项(第二次访存:查页表) 6. 由页面存放的内存块号、页内偏移量得到最终的物理地址 7. 访问目标内存单元(第三次访存) | 三次访存 |
访问一个逻辑地址需要几次访存?
与分页系统类似,分段系统中也可以引入快表机构,将近期访问过的段表项放到快表中,这样可以少一次访问,加快地址变换速度。
| 优点 | 缺点 | |
|---|---|---|
| 分页管理 | 内存空间利用率高,不会产生外部碎片, 只会有少量的页内碎片 | 不方便按照逻辑模块实现信息的共享和保护 |
| 分段管理 | 很方便按照逻辑模块实现信息的共享和保护 | 如果段长过大,为其分配很大的连续空间会很不方便。另外,段式管理会产生外部碎片 |
分页对用户是不可见的,分段对用户是可见的(但是不能说被用户所感知)
页的大小固定且由系统决定。段的长度却不固定,决定于用户编写的程序。
分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址。
分页管理当中,用户自己看来自己进程的地址空间是连续的。分段管理当中,用户也知道自己进程的地址空间是被分为一个一个段,并且每个段占据一连串的地址空间。
分段比分页更容易实现信息的共享和保护。要想实现信息共享,只需要让各个进程的段表项指向同一个段即可实现共享,可以被共享的代码称为纯代码或可重入代码(不属于临界资源)
传统存储管理:
连续分配:单一连续分配、固定分区分配、动态分区分配
非连续分配:基本分页存储管理、基本分段存储管理、基本段页式存储管理
传统存储管理的缺点:
虚拟内存就是应用了局部性原理,在程序装入时,可以将程序中很快会用到的部分装入内存,暂时用不到的部分留在外存,就可以让程序开始执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序。若内存空间不够,由操作系统负责将内存中暂时用不到的信息换出到外存。
虚拟内存有一下三个主要特征:
虚拟内存的实现需要建立在离散分配的内存管理方式基础上。
| 算法规则 | 优缺点 | |
|---|---|---|
| 最佳置换算法OPT | 优先淘汰最长时间内不会被访问的页面 | 缺页率最小,性能最好,但无法实现 |
| 先进先出置换算法FIFO | 优先淘汰最先进入内存的页面 | 实现简单,但性能很差,可能出现Belady异常 |
| 最近最久未使用算法LRU | 优先淘汰最近最久没访问的页面 | 性能很好,但算法开销大 |
| 时钟置换算法CLOCK | 循环扫描各页面 第一轮淘汰访问位 = 0的,并将扫描过的页面访问位改为1 若第一轮没选中,则进行第二轮扫描 | 实现简单,算法开销小,但未考虑页面是否被修改过 |
| 改进型CLOCK | 若用(访问位,修改位)的形式表述,则 第一轮:淘汰(0,0) 第二轮:淘汰(0,1),并将扫描过的页面访问位都置为0 第三轮:淘汰(0,0) 第四轮:淘汰(0,1) | 算法开销较小,性能也好 |
缺页时未必发生页面置换,若还有可用的空闲内存块,就不用进行页面置换。
驻留集:指的是请求分页存储管理中给进程分配的物理块的集合。在采用了虚拟存储技术的系统中,驻留集大小一般小于进程的总大小。
驻留集大小的分配有两种方式:
固定分配:驻留集大小不变。
可变分配:驻留集大小可变
置换页面范围又分为两种:
固定分配局部置换:进程运行前就分配一定数量的物理块,缺页时只能换出进程自己的某一页
可变分配全局置换:只要缺页就分配新物理块,可能来自空闲物理块,也可能需换出别的进程页面
可变分配局部置换:频繁缺页的进程,多分配一些物理块
何时调入页面:
从何处调入:
抖动(颠簸)现象:页面频繁换入换出的现象。主要原因是分配给进程的物理块不够(置换算法选择不当)
驻留集:指请求分页存储管理中给进程分配的存储块的集合
工作集:只在某段时间间隔里进程实际访问页面的集合

如上图访问到23,由于窗口尺寸为4,那么就会从当前位置开始,向前寻找4个页面号,由此来确定工作集的内容。
工作集大小可能小于窗口尺寸,实际应用中操作系统可以统计进程的工作集大小,根据工作集大小给进程分配若干内存块。
如:窗口尺寸为5,经过一段时间的监测,发现某进程的工作集最大为3,那么说明该进程有很好的局部性,可以给这个进程分配3个以上的内存块即可满足进程的运行需要。一般来说驻留集大小不能小于工作集大小,否则进程运行过程中将频繁缺页。
文件的逻辑结构分为无结构文件和有结构文件,有结构文件又分为顺序文件、索引文件、索引顺序文件。
根据各个记录是否按照关键字排列,可以把顺序文件分为串结构和顺序结构
若顺序文件采用链式存储,无论是定长还是可变长记录,都无法实现随机存取,每次只能从第一个记录开始依次向后查找。因为并不能直接计算出某一个记录的物理地址。
索引文件:为每个文件建立一张索引表,每个索引表的一条表项会对应文件的一条记录。文件的各个记录可以在物理上离散地存放,但是索引表的各个表项需要在物理上连续地存放。每个索引表的表项大小都是相等的,因此我们可以说索引表本身是定长记录的顺序文件(支持随机访问)
索引顺序文件:将记录分组,每组对应一个索引表项


count == 0 时才能真正删除文件数据和索引结点,否则会导致指针悬空C:/User/aaa(类似于Windows系统中的快捷方式)
文件分配方式分为:连续分配,链接分配,索引分配
| 文件分配方式 | How | 目录项内容 | 优点 | 缺点 |
|---|---|---|---|---|
| 连续分配 | 为文件分配的必须是连续的磁盘块 | (起始块号,文件长度) | 顺序存取速度快,支持随机访问 | 会产生碎片,不利于文件扩展 |
| 链接分配之隐式分配 | 除文件的最后一个盘块之外,每个盘块中都有指向下一个盘块的指针 | (起始块号,结束块号) | 可解决碎片问题,外存利用率高,文件扩展实现方便 | 只能顺序访问,不能随机访问 |
| 链接分配之显式分配 | 建立一张文件分配表(FAT),显式记录盘块的先后关系(开机后FAT常驻内存) | 起始块号 | 可解决碎片问题,外存利用率高,文件扩展实现方便,且可实现随机访问 | FAT需要占用一定的存储空间 |
| 索引分配 | 为文件数据块建立索引表,若文件太大,可采用链接方案、多层索引、混合索引 | 链接方案记录的是第一个索引块的块号,多层/混合索引记录的是顶级索引块的块号 | 支持随机访问,易于实现文件的扩展 | 索引表需占用一定的存储空间。访问数据块前需要先读入索引块。若采用链接方案,查找索引块时可能需要很多次读磁盘操作。 |
若采用多层索引,则各层索引表大小不能超过一个磁盘块
采用K层索引结构,且顶级索引表未调入内存,则访问一个数据块只需要K+1次读磁盘操作


位示图法:如何从(字号,位号)推出盘块号,如何从盘块号推出(字号,位号)
采用位示图法如何进行分配呢?若某个文件需要K个空闲块:
如何回收磁盘块呢?
创建文件:操作系统在处理Create系统调用时,主要做了两件事:
删除文件:操作系统在处理Delete系统调用时,主要做了几件事:
根据文件存放路径找到相应的目录文件,从目录中找到文件名对应的目录项,从目录表中删除文件对应的目录项,删除文件对应的文件控制块
根据该目录项记录的文件在外存的存放位置、文件大小等信息,回收文件占用的磁盘块。
打开文件:操作系统在处理open系统调用时,主要做了几件事(如下图):
关闭文件:操作系统在处理Close的系统调用时,主要做了几件事:
读文件:操作系统在处理read系统调用时,会从读指针指向的外存中,将用户指定大小的数据读入用户指定的内存区域中
写文件:操作系统在处理write系统调用时,会从用户指定的内存区域中,将指定大小的数据写回写指针指向的外存
读/写文件用 “文件描述符"即可指明文件,不再需要用到"文件名”
块设备【传输速率较高,可寻址,即对它可随机地读/写任一块】
字符设备【传输速率较慢,不可寻址,在输入/输出时常采用中断驱动方式】
| 完成一次读/写的过程 | CPU干预频率 | 每次I/O的数据传输单位 | 数据流向 | |
|---|---|---|---|---|
| 程序直接控制方式 | CPU发出I/O命令后需要不断轮询 | 极高 | 字 | 设备->CPU->内存 内存->CPU->设备 |
| 中断驱动方式 | CPU发出I/O命令后可以做其他事,本次I/O完成后设备控制器发出中断信号 | 高 | 字 | 设备->CPU->内存 内存->CPU->设备 |
| DMA方式 | CPU发出I/O命令后可以做其他事,本次I/O完成后DMA控制器发出中断信号 | 中 | 块 | 设备->内存 内存->设备 |
| 通道控制方式 | CPU发出I/O命令后可以做其他事,通道会执行通道程序以完成I/O,完成后通道向CPU发出中断信号 | 低 | 一组块 | 设备->内存 内存->设备 |
程序直接控制方式:当用户进程需要输入或输出数据时,它通过CPU发出启动设备的指令,然后用户进程进入测试等待状态,在等待时间内,CPU不断地用一条测试指令,通过测试一台设备的忙/闲标志来获得外设的工作状态
中断驱动方式:仅当I/O操作正常或异常结束时,才中断中央处理机
DMA方式:在外围设备和内存之间开辟直接的数据交换通路
当I/O任务完成时,I/O控制器会发送一个中断信号,系统会根据中断信号类型找到相应的中断处理程序并执行。

中断处理程序会从I/O控制器当中读出设备的状态来判断这次的I/O是不是正常的结束,如果此时正常的结束,接下来中断处理程序会从I/O控制器的数据寄存器当中读出一个字的数据,并且经由CPU放到内存缓冲区当中,这就完成了一个字的读入。若这次的I/O是非正常的结束,那么系统会根据异常原因做相应的处理。这就是中断处理程序所需要做的事情。
当中断处理程序做完后,接下来又会交由设备驱动程序处理,再交给设备独立性软件处理,最终反映给用户。所以对于数据的处理是从下向上依次层层处理的。

直接涉及到硬件具体细节、且与中断无关的操作肯定是在设备驱动程序层完成的;没有涉及硬件的、对各种设备都需要进行的管理工作都是在设备独立性软件层完成的

假脱机技术,又称 SPOOLing技术,是用软件的方式模拟脱机技术。SPOOLing系统的组成如下:

SPOOLing系统主要包括三部分,即输入井和输出井,输入缓冲区和输出缓冲区以及输入进程和输出进程。这三部分由预输入程序,井管理程序和缓输出程序管理
SPOOLing技术是为了缓和CPU与I/O设备之间速度不匹配的矛盾
输入进程是先接收输入设备的数据,然后把这些数据先放到输入缓冲区里,之后再把输入缓冲区的数据放到磁盘的输入井当中。输出进程是先从输出井当中取出数据放到输出缓冲区,再将输出缓冲区的数据放到输出设备。
SPOOLing技术需要外存的支持,需要多道程序技术的支持
SPOOLing技术是以空间换时间
SPOOLing技术可以把一台物理设备虚拟成逻辑上的多台设备,可将独占式设备改造成共享设备。
SPOOLing技术不需要等待I/O操作完成
设备的固有属性可分为三种:独占设备、共享设备、虚拟设备
独占设备 —— 一个时段只能分配给一个进程(如打印机)
共享设备 —— 可同时分配给多个进程使用(如磁盘),各进程往往是宏观上同时共享使用设备,而微观上交替使用。
虚拟设备 —— 采用SPOOLing技术将独占设备改造成虚拟的共享设备,可同时分配给多个进程使用(如采用SPOOLing技术实现的共享打印机)
独占设备采用静态分配方式,共享设备采用动态分配方式
从进程运行的安全性上考虑,设备分配有两种方式:
安全分配方式:为进程分配一个设备后就将进程阻塞,直到进程释放I/O设备后才将进程唤醒。(例如考虑进程请求打印机打印输出的例子),一个进程被分配打印机之后,这个进程就必须先阻塞等待,虽然说进程只需要把数据丢给打印机然后就不用管打印机的打印输出过程,继续向下执行后面代码。但是采用安全分配方式就必须阻塞,直到打印结束之后进程才会被再次唤醒。
不安全分配方式:进程发出I/O请求后,系统为其分配I/O设备,进程可继续执行,之后还可以发出新的I/O请求。只有某个I/O请求得不到满足时才将进程阻塞。(一个进程被分配打印机之后,这个进程把数据丢给打印机,然后自己继续向下执行,甚至再向下执行的过程中还可以继续申请其他I/O设备)
静态分配:进程运行前为其分配全部所需资源,运行结束后归还资源。(破坏了"请求和保持"条件,不会死锁)
动态分配:进程运行过程中动态申请设备资源
设备分配步骤:
根据进程请求的逻辑设备名查找系统设备表SDT(注:用户编程时提供的逻辑设备名其实就是设备类型)
查找系统设备表SDT,找到用户进程指定类型的、并且空闲的设备,将其分配给该进程。操作系统在逻辑设备表(LUT)中新增一个表项。
根据通道控制表DCT找到控制器控制表COCT,若控制器忙碌则将进程PCB挂到控制器等待队列中,不忙碌则将控制器分配给进程
根据控制器控制表COCT找到设备控制表CHCT,若通道忙碌则将进程PCB挂到通道等待队列中,不忙碌则将通道分配给进程。
逻辑设备表(LUT)建立了逻辑设备名与物理设备名之间的映射关系。
设备分配程序为用户进程分配设备的过程通常是
- 先分配设备
- 再分配设备控制器
- 最后分配通道
缓冲区是一个存储区域,一般情况下,更多的是利用内存作为缓冲区

单缓冲:采用单缓冲策略,处理一块数据平均耗时Max(C, T)+M
双缓冲:采用双缓冲策略,处理一个数据块的平均耗时为Max (T, C+M)
若两台主机只设置单缓冲区,在任意时刻只能实现数据的单向传输。为了实现双向传输,我们可以给两台机器配置双缓冲区,其中一个缓冲区用来暂存即将发送的数据,另一个缓冲区用于接收输入的数据。
当缓冲区数据非空时,不能往缓冲区冲入数据,只能从缓冲区把数据传出;当缓冲区为空时,可以往缓冲区冲入数据,但必须把缓冲区充满以后,才能从缓冲区把数据传出
缓冲区是为了缓和CPU和外部设备速度不匹配的矛盾,提高CPU和外部设备工作的并行性
缓冲技术中的缓冲池是在主存中,文件系统中的交换区是在外存中
缓冲区管理者需要考虑的重要问题是同步问题,因为缓冲区是一种临界资源

一片磁盘就是一个盘面,盘面上一圈就是磁道,每个磁道分为若干扇区,每个扇区就是一个磁盘块,各个扇区存放的数据量相同。
可用(柱面号,盘面号,扇区号)来定位任意一个“磁盘块”,可根据该地址读取一个“块”:
按照磁头是否可以移动分为:
按照盘片是否可更换分为:
根据柱面号大小的优先级来进行调度,柱面号最大的最先调度
**寻找时间(寻道时间)**TS:在读/写数据前,将磁头移动到指定磁道所花的时间。
则寻道时间:Ts = s + m×n
延迟时间TR:通过旋转磁盘,使磁头定位到目标扇区所需要的时间。设磁盘转速为r(单位:转/秒,或转/分),则平均所需的延迟时间TR=(1/2)×(1/r)=1/2r
传输时间Tt:从磁盘读出或向磁盘写入数据所经历的时间,假设磁盘转速为r,此次读/写的字节数为b,每个磁道上的字节数为N。则:传输时间Tt=(1/r)×(b/N)=b(rN)
则总的平均存取时间Ta = Ts + 1/2r +b/(rN)
- 延迟时间和传输时间都与磁盘转速相关,且为线性相关。而转速是硬件的固有属性,因此操作系统也无法优化延迟时间和传输时间。
- 采用交替编号的策略减少延迟时间
- 采用错位命名的策略减少延迟时间
- 但是操作系统的磁盘调度算法会直接影响寻道时间
先来先服务算法(FCFS):根据进程请求访问磁盘的先后顺序进行调度。
最短寻找时间优先(SSTF):SSTF算法会优先处理的磁道是与当前磁头最近的磁道。可以保证每次的寻道时间最短,但是并不能保证总的寻道时间最短。
扫描算法:磁头有可能在一个小区域内来回来去地移动。为了防止这个问题,可以规定,只有磁头移动到最外侧磁道的时候才能往内移动,移动到最内侧磁道的时候才能往外移动。这就是扫描算法(SCAN)的思想。由于磁头移动的方式很像电梯,因此也叫电梯算法。
LOOK调度算法:如果在磁头移动方向上已经没有别的请求,就可以立即改变磁头移动方向。(边移动边观察,因此叫LOOK)
循环扫描算法(C-SCAN):SCAN算法对于各个位置磁道的响应频率不平均,而C-SCAN算法就是为了解决这个问题。规定只有磁头朝某个特定方向移动时才处理磁道访问请求,而返回时直接快速移动至起始端而不处理任何请求。
优点:比起SCAN来,对于各个位置磁道的响应频率很平均。
缺点:只有到达最边上的磁道时才能改变磁头移动方向,比起SCAN算法来,平均寻道时间更长。
C-LOOK算法:C-SCAN算法的主要缺点是只有到达最边上的磁道时才能改变磁头移动方向,并且磁头返回时不一定需要返回到最边缘的磁道上。C-LOOK算法就是为了解决这个问题。如果磁头移动的方向上已经没有磁道访问请求了,就可以立即让磁头返回,并且磁头只需要返回到有磁道访问请求的位置即可。
