第 1 章 计算机系统概述
- 操作系统(OS)是软件和硬件资源的管理者
- 计算机系统自下而上:硬件、操作系统、应用程序和用户
多道运行的特征:
优点:提高CPU的利用率,充分发挥计算机系统部件的并行性
缺点:作业周转时间长
- 多道
- 宏观上并行
- 微观上串行
第 2 章 进程与线程
2.1 进程与线程
1、进程与程序:
- 进程:动态的,程序的一次执行过程
- 同一个程序多次执行就会对应多个进程
2、PID(Process ID)进程ID
每创建一个进程,操作系统会为其分配唯一的PID
3、PCB(Process Control Block)进程控制快
PCB是进程存在的标志,当进程被创建,操作系统就会为其创建PCB,当进程结束时,操作系统就会回收其占用的PCB
PCB实际上也是一种数据结构,里面主要存放了进程描述信息(PID、UID等)、进程控制和管理信息(CPU、内存、磁盘、网络等使用情况,进程当前的状态)、资源分配清单、处理机相关信息等
4、进程的特征
动态性是最基本的特征
特征 | 描述 |
---|---|
动态性 | 进程是程序的一次执行过程,是动态消亡的 |
并发性 | 内存有多个进程实体,是可以并发执行的 |
独立性 | 进程可以独立运行、独立分配资源、独立接受调度 |
异步性 | “进程同步机制”解决异步问题 |
独立性 | 每个进程都会配置一个PCB。进程由PCB、程序段和数据段组成 |
5、进程的状态与转换
- 创建态:进程正在被创建
- 就绪态:系统分配了资源,在等上CPU
- 运行态:进程正在CPU上跑
- 阻塞态:进程因为等待某外部因素而暂停运行
- 终止态:进程结束,释放资源
2.2 进程调度
2.2.1 三级调度
- 高级调度(作业调度)---->一定发生,频率低
磁盘中的作业首次被调入内存 - 中级调度(内存调度)---->可能发生
内存中的作业和磁盘交换数据,提高内存的利用率(内存不够) - 低级调度(进程调度)---->一定发生,频率很高
从就绪队列中选取一个进程上CPU运行(争夺CPU)
2.2.2 经典调度算法
吞吐量:一定时间内运行程序数量
- 先来先服务(FCFS)算法
算法每次从就绪队列中选择最先进入该队列的进程,让它上CPU
调度方式:非抢占
调度时机:进程结束时
算法特点:算法简单,效率低,利长不利短,利CPU忙不利I/O忙,不适用于分时系统和实时系统
- 短作业优先(SJF)算法
短作业优先(SJF):每次从后备队列中选择一个或多个估计运行时间最短的作业,将它们调入内存运行,分配必要的资源,创建进程并放入就绪队列;
短进程优先(SPF):每次从就绪队列(内存)中选择一个估计运行时间最短的作业,让它上CPU运行;
调度方式:非抢占
调度时机:进程结束时、阻塞时(I/O请求)
算法特点:在就绪队列中的进程可插队(动态改变)、平均等待时间,平均周转时间最少、利短不利长(长易“饥饿”)、不能保证紧迫作业及试运行(程序员可提供虚假运行时间)、吞吐量大
- 优先级调度算法
优先级作业调度:每次从后备队列中选择一个或多个优先级最高的作业,将它们调入内存运行,分配必要的资源,创建进程并放入就绪队列;
优先级进程调度:每次从就绪队列(内存)中选择一个优先级最高的作业,让它上CPU运行;
调度算法的分类
(1) 按照优先级进程能否抢占分类
- 非抢占式
当一个进程在CPU运行时,即使有优先级更高的进程进入就绪队列,也要等当前进程运行完毕才能上CPU运行- 抢占式
当一个进程在CPU运行时,有优先级更高的进程进入就绪队列,立即停止当前进程,让出CPU供优先级更高的进程运行(2) 按照进程创建后优先级是否可改变分类
- 静态优先级
优先级在创建进程时确定,且在进程的整个运行过程期间保持不变
主要依据:进程类型、进程对资源的要求、用户要求- 动态优先级
在进程的运行过程中,根据进程情况的变化动态调整优先级
主要依据:进程占有CPU时间长短、就绪进程等待CPU时间长短
调度方式:非抢占、抢占
调度时机:进程结束时、阻塞时(I/O请求)、更高优先级进程进入就绪队列
算法特点:满足紧迫作业要求、特别适合用在实时系统中
- 高相应比优先调度算法
主要用于作业调度,对FCFS调度算法和SJF调度算法的综合平衡
响应比 = (等待时间+运行时间)/ 运行时间
调度方式:无(用于作业调度)
调度时机:无
算法特点:克服了长作业饥饿,又能让短作业先运行
- 时间片轮转调度算法
主要适用于分时系统,系统将所有的就绪进程按FCFS排成一个就绪队列,一个进程只允许运行一个固定的时间片,如50ms。在使用完一个时间片后,即使进程未完成,也需要释放出CPU,让给下一个进程,被剥夺的进程进入就绪队列尾部,等候再次运行。
调度方式:抢占
调度时机:时间片用完、进程结束
算法特点:适用于分时系统、时间片大小选择很关键,太大退化为FCFS,太小CPU开销大
- 多级反馈队列调度算法
是优先级调度算法和时间片轮转调度算法的综合与发展
(1)设置多个就绪队列,每个队列优先级由高到低;
(2)每个队列的时间片大小不同,优先级越高的队列,时间片越小;
(3)每个队列采用FCFS算法,当进程被降到第n级队列,在第n级队列采用时间片轮转算法;
(4)按队列优先级调度,仅当第 i 级队列为空时,才可以调度第 i+1 级队列。当CPU正在运行第 i 级某进程时,有新进程进入优先级更高的队列,此时,CPU将立即终止运行当前进程,并将当前进程放入第 i 级队列末尾,转而运行优先级更高的进程。
调度方式:综合
调度时机:综合
算法特点:短作业优先完成(等待时间少)、周转时间少、避免长作业“饥饿”
2.2.3 调度算法的性能评价标准
- CPU利用率
- 周转时间 = 完成-提交 = 等待+运行
2.3 同步与互斥
- 临界资源:一次只允许一个进程使用的资源
- 临界资源的访问过程分为四个部分:
(1)进入区:检查进程是否可进入临界区。若能进入,设置临界区正在访问的标志,已阻止其他进程同时进入临界区。
(1)临界区:进程中访问临界资源的代码。
(1)退出区:清除临界区正在访问的标志。
(1)剩余区:代码中的剩余部分。
// 伪代码
do{
entry section;// 进入区
critical section;// 临界区
exit section;// 退出区
remainder section;// 剩余区
}while(true)
- Peterson’s 算法
flag[i] = true;// i想要进入临界区
turn = j;// 让 j 进入临界区
2.3.1 信号量
用交通信号灯模仿信号量
// 记录型信号量数据结构
typedef struct{
int value;
struct process *L;
}semaphore;
// P操作
void P(semaphore S){ // 相当于申请资源
S.value--;
if(S.value<0){
add this process to S.L;
block(S.L);// block原语,进行自我阻塞,放弃CPU
}
}
// V操作
void V(semaphore S){ // 相当于申请资源
S.value++;
if(S.value<=0){
remove a process P from S.L;
wakeup(P);// 将排在阻塞队列L中的第一个进程P唤醒,让它上CPU运行
}
}
- 信号量实现进程同步(“先V,P后”)
保证 P0 进程中的 x 语句先执行, P1 进程中的 y 语句后执行
Tip:先执行的语句放在 V 之前,后执行的语句放在P之后
定义P0和P1的公共信号量 S 的初值为0
// S = 0
P0(){
x;
V(S);
....;
}
P1(){
....;
P(S);
y;
}
- case1:先执行P0
P0:先执行 x 语句,随后执行 V 操作,此时S=1;
P1:执行 P 操作,S>0,则直接执行 y 语句。- case2:先执行P1
P1:先执行 P 操作,此时S=-1,P0进入阻塞队列S.L;
P0:执行 x 语句,随后执行 V 操作,S=0,唤醒P1;
P1:继续执行 y 语句。
- 信号量实现进程互斥(“P临V”)
将临界区置于P(S) 与 V(S)之间
定义P0和P1的公共信号量 S 的初值为1
// S = 1
P0(){
....;
P(S);
P0的临界区;
V(S);
....;
}
P1(){
....;
P(S);
P1的临界区;
V(S);
....;
}
- 当没有进程处于临界区时,P0想要进入临界区,首先执行 P 操作,将S=0,随后,P0进入临界区;
- 当P1想要进入临界区时,首先执行 P 操作,S=-1,P1被阻塞进入S.L队列;
- 等到P0退出临界区,执行 V 操作,S=0,唤醒P1,P1方可进入临界区。
2.3.2 经典同步问题
- 进程模版:
P0(){
while(1){
....;
}
}
P1(){
while(1){
....;
}
}
(1)生产者消费者问题
特征:
(1)有容器,且容器 <= 容量
(2)有生产行为(容器内东西越来越多)和消费行为(容器内东西越来越少)
分析:
(1)生产者 -> 剩余空间
(2)消费者 -> 已占用空间
- 生产者消费者问题进程描述:
semaphore mutex=1;// 互斥信号量(有多少个容器,就要有多少个互斥信号量)
semaphore empty=n;// 生产者关心 -> 剩余空间信号量
semaphore full=0; // 消费者关心 -> 当前已占用空间
producer(){
while(1){
....;
P(empty);// 要什么,P一下
P(mutex);// 互斥加紧
add data to full; // 行为
V(mutex);// 互斥加紧
V(full);// 提供了什么,V一下
....;
}
}
consumer(){
while(1){
....;
P(full);
P(mutex);// 互斥加紧
remove data from full;
V(mutex);// 互斥加紧
V(empty);
....;
}
}
(2)读者写者问题
特征:
(1)多个“读者”(不对资源造成改变)同步
(2)多个“写者”(对资源造成改变)互斥
(3)“读者”和“写者”互斥
分析:
- 写者 —> 关心文件是否被占用
- 读者团:
第一个读者 —> 关心文件是否被占用
中间读者 —> 改变读者团数量
最后一个读者 —> 关心文件是否被占用
- 读者写者问题进程描述:
int count=0;// 读者数量
semaphore file=1;// 文件互斥访问信号量
semaphore mutex=1;// 变量互斥访问信号量
writer(){
while(1){
P(file);// 文件是否被占用
writing;// 写文件
V(file);// 释放文件
}
}
reader(){
while(1){
P(mutex);// 变量互斥访问
// 是否为第一个读者
if(count==0){
P(file);// 第一个读者访问文件是否被占用
}
count++;// 读者数量+1
V(mutex);
reading;// 读文件
....;
P(mutex);// 变量互斥访问
count--;// 读者数量-1
// 是否为最后一个读者
if(count==0){
V(file);// 最后一个读者释放文件
}
V(mutex);
}
}
- P,V操作解题技巧
(1)画图理解题目
(2)判断题目类型
- 题目类型:
生产者消费者----60%
读者写者----30%(3)分析进程数目,填写进程模版
(4)补充基本代码
(5)补充P,V代码
(6)调整检查代码
Tip1:变量一定要互斥
Tip2:对容器产生影响的操作一定要互斥
Tip3:有多少个容器就有多少个互斥信号量
2.4 死锁
2.4.1 死锁逻辑图
2.4.2 死锁、活锁、饥饿
死锁:资源被占有,双方不礼让,永远等下去
活锁:资源未被占有,双方礼让,永远等下去
饥饿:一方占用资源,另一方没机会使用资源,但不会永远等下去
2.4.3 死锁原因及必要条件
2.4.4 死锁处理策略
死锁避免 -----> 银行家算法
死锁检测和解除 -----> 资源分配图
- 圆形为进程,矩形为资源
- 进程的入边为资源已分配边,进程的出边为资源申请边
- 化简资源分配表
Step1:寻找进程
Step2:判断申请边(进程出边)是否都能得到满足。若能得到满足,则删除该进程及其所有边;若不能得到满足,寻找下一个进程,重复Step2
Step3:若所有进程均已处理,资源分配图中无剩余边,则说明无死锁;有剩余边,系统存在死锁
得到满足:申请资源 Avaliable >= Request
第 3 章 内存管理
- 1GB = 230 B
Tips:
1、m(bit)最多存储 2m 个内存块(0~2m - 1)
2、J号内存块的起始地址 = J * 内存块大小
分页存储
- 页框(内存块)----分的是内存空间
- 页、页面-----分的是进程的逻辑地址空间(大小等于页框大小)
分页存储逻辑地址转化为物理地址
逻辑地址对应的物理地址 = 页面在内存中的起始地址 + 页内偏移量
- 页号:逻辑地址 / 页面大小 ------> Math.floor()
- 页内偏移量:逻辑地址 % 页面大小 ------> 取余
!!!页面大小刚好是 2 的整数幂
若每个页面大小是2k B,用二进制数表示逻辑地址,则末尾 k 位就是页内偏移量,其余部分就是页号
物理地址 = 页框号*页长 + 页内地址
- Step1:根据逻辑地址计算出页号和页内地址(页面偏移量)
- Step2:判断页号是否越界
- Step3:查询页表,找到页号对应的页表项,确定页框号(内存块号)
- Step4:根据页框号计算出初始地址,加上页内地址,得到物理地址
分段存储
分段系统的逻辑地址结构,由段号和段内地址(段内偏移量)组成
- 段号的位数(2k)决定了每个进程可以分为多少个段
- 段内地址的位数(2k)决定了每个段的最大长度
分段存储逻辑地址转化为物理地址
物理地址 = 段基址 + 段内地址
- Step1:根据逻辑地址计算出段号和段内地址
- Step2:段号与段表长度比较,判断段号是否越界
- Step3:查询段表,找到段号对应的段表项
- Step4:段内地址与段表项段长比较,判断段内地址是否越界
- Step5:根据段基址加上段内地址,得到物理地址
需要注意的是:在根据段号找到段表项后,一定需要将段内地址与段长相比较,如果W>=C,则表示越界中断。
程序局部性访问原理:
- 时间局部性:如果某条指令被执行或某个数据结构被访问,则一段时间内,该指令、数据结构可能再次被访问;
- 空间局部性:如果某个存储单元被访问,不久之后,附近的存储单元也可能被访问;
- 顺序局部性:CPU跟踪程序的执行是按照在内存中的连续地址进行的。
分段 VS 分页
- 共同点:都采用离散分配方式
- 不同点:
(1)页是信息的物理单位,段是信息的逻辑单位;
(2)页的大小固定且由系统确定,段长不固定,取决于用户所编写的程序;
(3)页式管理的进程地址空间是一维的,段式管理的进程地址空间是二维的。
页面置换算法
(1)最佳(OPT)
淘汰已经在内存中,但未来最长时间内不再访问的页面
(2)先进先出(FIFO)
最先进来的最先被置换,淘汰在内存中滞留时间最长的页
(3)最近最少使用(LRU)
- 例题:有一个虛拟存储系统。分配给某进程 3 页内存,开始时内存为空,页面访问序列如下:6、5、4、3、2、1、5、1、5、2、1、2、1、2、1、6、5
(1) 若采用先进先出的页面置换算法(FIFO),缺页次数为多少?置换次数为多少?
(2) 若采用 OPT,缺页次数为多少?置换次数为多少?
(3) 若采用最近 最少使用的页面置换算法(LRU),缺页次数为多少?置换次数为多少?
第 4 章 文件管理
文件的物理结构
- 连续分配
优点:实现简单、存取速度快
缺点:文件长度不宜动态增加,会产生外部碎片 - 链接分配
(1)隐式链接
缺点:只适合顺序访问
(2)显式链接
优点:显著提高了检索速度,也减少了磁盘访问的次数 - 索引分配
优点:支持直接访问,没有外部碎片。
缺点:增加了系统存储空间的开销
第 5 章 (输入/输出)I/O管理
磁盘调度算法
- 先来先服务(FCFS)
- 最短寻找时间优先(SSTF)
- 扫描 / 电梯调度(SCAN)
- 循环扫描(C-SCAN)
算法 | 特点 |
---|---|
先来先服务(FCFS) | 优:公平、简单 | 缺:平均寻道距离大 |
最短寻找时间优先(SSTF) | 优:性能优于FCFS | 缺:可能出现“饥饿”现象 |
扫描 / 电梯调度(SCAN) | 优:性能较好,可避免“饥饿” | 缺:不利于远离磁头一端的访问请求 |
循环扫描(C-SCAN) | 优:消除了对两端磁道请求的不公平 | 缺:- |