导图社区 操作系统
P1来源于CS-Notes笔记整理(用于面试),P2-P4来源于王道教材(更为深入,用于考研),经过精心的整理和排版。
编辑于2021-05-23 12:06:47OS(CS-Notes)
概述
基本特征
1. 并发
并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。
并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。
操作系统通过引入进程和线程,使得程序能够并发运行。
2. 共享
共享是指系统中的资源可以被多个并发进程共同使用。
有两种共享方式:互斥共享和同时共享
互斥共享的资源称为临界资源,例如打印机等,在同一时刻只允许一个进程访问,需要用同步机制来实现互斥访问。
3. 虚拟
虚拟技术把一个物理实体转换为多个逻辑实体。
主要有两种虚拟技术:时分复用技术和空分复用技术。
多个进程能在同一个处理器上并发执行使用了时分复用技术,让每个进程轮流占用处理器,每次只执行一小个时间片并快速切换。
虚拟内存使用了空分复用技术,它将物理内存抽象为地址空间,每个进程都有各自的地址空间。地址空间的页被映射到物理内存,地址空间的页并不需要全部在物理内存中,当使用到一个没有在物理内存的页时,执行页面置换算法,将该页置换到内存中。
4. 异步
异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进。
基本功能
1. 进程管理
进程控制、进程同步、进程通信、死锁处理、处理机调度等。
2. 内存管理
内存分配、地址映射、内存保护与共享、虚拟内存等。
3. 文件管理
文件存储空间的管理、目录管理、文件读写管理和保护等。
4. 设备管理
完成用户的 I/O 请求,方便用户使用各种设备,并提高设备的利用率。
主要包括缓冲管理、设备分配、设备处理、虛拟设备等。
系统调用
如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。
Linux 的系统调用主要有以下这些:
Task Commands 进程控制 fork(); exit(); wait(); 进程通信 pipe(); shmget(); mmap(); 文件操作 open(); read(); write(); 设备操作 ioctl(); read(); write(); 信息维护 getpid(); alarm(); sleep(); 安全 chmod(); umask(); chown();
宏内核和微内核
1. 宏内核
宏内核是将操作系统功能作为一个紧密结合的整体放到内核。
由于各模块共享信息,因此有很高的性能。
2. 微内核
由于操作系统不断复杂,因此将一部分操作系统功能移出内核,从而降低内核的复杂性。移出的部分根据分层的原则划分成若干服务,相互独立。
在微内核结构下,操作系统被划分成小的、定义良好的模块,只有微内核这一个模块运行在内核态,其余模块运行在用户态。
因为需要频繁地在用户态和核心态之间进行切换,所以会有一定的性能损失。
中断分类
1. 外中断
由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。
2. 异常
由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。
3. 陷入
在用户程序中使用系统调用。
进程管理
进程与线程
1. 进程
进程是资源分配的基本单位。
进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB 的操作。
下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。
2. 线程
线程是独立调度的基本单位。
一个进程中可以有多个线程,它们共享进程资源。
QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。
3. 区别
Ⅰ 拥有资源
进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属进程的资源。
Ⅱ 调度
线程是独立调度的基本单位,在同一进程中,线程的切换不会引起进程切换,从一个进程中的线程切换到另一个进程中的线程时,会引起进程切换。
Ⅲ 系统开销
由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等,所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程 CPU 环境的保存及新调度进程 CPU 环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。
Ⅳ 通信
线程间可以通过直接读写同一进程中的数据进行通信,但是进程通信需要借助 IPC。
进程状态的切换
就绪状态(ready):等待被调度
运行状态(running)
阻塞状态(waiting):等待资源
注意
只有就绪态和运行态可以相互转换,其它的都是单向转换。就绪状态的进程通过调度算法从而获得 CPU 时间,转为运行状态;而运行状态的进程,在分配给它的 CPU 时间片用完之后就会转为就绪状态,等待下一次调度。
阻塞状态是由运行状态(缺少需要的资源)转换而来,但是该资源不包括 CPU 时间,缺少 CPU 时间会从运行态转换为就绪态。
进程调度算法
不同环境的调度算法目标不同,因此需要针对不同环境来讨论调度算法。
1. 批处理系统
批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。
1.1 先来先服务 first-come first-serverd(FCFS)
非抢占式的调度算法,按照请求的顺序进行调度。
有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。
1.2 短作业优先 shortest job first(SJF)
非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
1.3 最短剩余时间优先 shortest remaining time next(SRTN)
最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。
2. 交互式系统
交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。
2.1 时间片轮转
将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
时间片轮转算法的效率和时间片的大小有很大关系:
因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。
而如果时间片过长,那么实时性就不能得到保证。
2.2 优先级调度
为每个进程分配一个优先级,按优先级进行调度。
为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
2.3 多级反馈队列
一个进程需要执行 100 个时间片,如果采用时间片轮转调度算法,那么需要交换 100 次。
多级队列是为这种需要连续执行多个时间片的进程考虑,它设置了多个队列,每个队列时间片大小都不同,例如 1,2,4,8,..。进程在第一个队列没执行完,就会被移到下一个队列。这种方式下,之前的进程只需要交换 7 次。
每个队列优先权也不同,最上面的优先权最高。因此只有上一个队列没有进程在排队,才能调度当前队列上的进程。
可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。
3. 实时系统
实时系统要求一个请求在一个确定时间内得到响应。
分为硬实时和软实时,前者必须满足绝对的截止时间,后者可以容忍一定的超时。
进程同步
1. 临界区
对临界资源进行访问的那段代码称为临界区。
为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。
// entry section // critical section; // exit sectionCopy to clipboardErrorCopied
2. 同步与互斥
同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。
互斥:多个进程在同一时刻只有一个进程能进入临界区。
3. 信号量
信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作
down
:如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0;
up
对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。
down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。
如果信号量的取值只能为 0 或者 1,那么就成为了互斥量(Mutex) ,0 表示临界区已经加锁,1 表示临界区解锁。
typedef int semaphore; semaphore mutex = 1; void P1() { down(&mutex); // 临界区 up(&mutex); }
void P2() { down(&mutex); // 临界区 up(&mutex); }
使用信号量实现生产者-消费者问题
问题描述:使用一个缓冲区来保存物品,只有缓冲区没有满,生产者才可以放入物品;只有缓冲区不为空,消费者才可以拿走物品。
因为缓冲区属于临界资源,因此需要使用一个互斥量 mutex 来控制对缓冲区的互斥访问。
为了同步生产者和消费者的行为,需要记录缓冲区中物品的数量。数量可以使用信号量来进行统计,这里需要使用两个信号量:empty 记录空缓冲区的数量,full 记录满缓冲区的数量。其中,empty 信号量是在生产者进程中使用,当 empty 不为 0 时,生产者才可以放入物品;full 信号量是在消费者进程中使用,当 full 信号量不为 0 时,消费者才可以取走物品。
分析图
生产者
生产
放置
empty, mutex
注意现有empty,才会占用缓冲区锁,否则死锁
mutex, full
保证缓冲区最小占用,先释放
消费者
取走
full, mutex
mutex, empty
消费
empty=N, full=0, mutex=1
4. 管程
使用信号量机制实现的生产者消费者问题需要客户端代码做很多控制,而管程把控制的代码独立出来,不仅不容易出错,也使得客户端代码调用更容易。
管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。
syncronized func()
管程引入了条件变量 以及相关的操作: wait() 和 signal() 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。
经典同步问题
1. 生产者和消费者问题
2. 哲学家进餐问题
五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。
下面是一种错误的解法,如果所有哲学家同时拿起左手边的筷子,那么所有哲学家都在等待其它哲学家吃完并释放自己手中的筷子,导致死锁。
#define N 5 void philosopher(int i) { while(TRUE) { think(); take(i); // 拿起左边的筷子 take((i+1)%N); // 拿起右边的筷子 eat(); put(i); put((i+1)%N); } }
为了防止死锁的发生,可以设置两个条件:
1. 必须同时拿起左右两根筷子;
2. 只有在两个邻居都没有进餐的情况下才允许进餐。
3. 读者-写者问题
允许多个进程同时对数据进行读操作,但是不允许读和写以及写和写操作同时发生。
一个整型变量 count 记录在对数据进行读操作的进程数量,一个互斥量 count_mutex 用于对 count 加锁,一个互斥量 data_mutex 用于对读写的数据加锁。
实现
读者
请求读
w, (count++=0?)rw
w
读
(--count=0?)rw
写者
请求写+写
w, rw
rw, w
typedef int semaphore; semaphore count_mutex = 1; semaphore data_mutex = 1; int count = 0;
void reader() { while(TRUE) { down(&count_mutex); count++; if(count == 1) down(&data_mutex); // 第一个读者需要对数据进行加锁,防止写进程访问 up(&count_mutex); read(); down(&count_mutex); count--; if(count == 0) up(&data_mutex); up(&count_mutex); } }
void writer() { while(TRUE) { down(&data_mutex); write(); up(&data_mutex); } }
进程通信
进程同步与进程通信很容易混淆,它们的区别在于:
进程同步:控制多个进程按一定顺序执行
进程通信:进程间传输信息
进程通信是一种手段,而进程同步是一种目的。也可以说,为了能够达到进程同步的目的,需要让进程进行通信,传输一些进程同步所需要的信息。
1. 管道
管道是通过调用 pipe 函数创建的,fd[0] 用于读,fd[1] 用于写。
#include <unistd.h> int pipe(int fd[2]);
它具有以下限制:
只支持半双工通信(单向交替传输);
只能在父子进程或者兄弟进程中使用。
2. FIFO
也称为命名管道,去除了管道只能在父子进程中使用的限制。
#include <sys/stat.h> int mkfifo(const char *path, mode_t mode); int mkfifoat(int fd, const char *path, mode_t mode);
FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。
3. 消息队列
相比于 FIFO,消息队列具有以下优点:
1. 消息队列可以独立于读写进程存在,从而避免了 FIFO 中同步管道的打开和关闭时可能产生的困难;
2. 避免了 FIFO 的同步阻塞问题,不需要进程自己提供同步方法;
3. 读进程可以根据消息类型有选择地接收消息,而不像 FIFO 那样只能默认地接收。
4. 信号量
它是一个计数器,用于为多个进程提供对共享数据对象的访问。
5. 共享存储
允许多个进程共享一个给定的存储区。因为数据不需要在进程之间复制,所以这是最快的一种 IPC。
需要使用信号量用来同步对共享存储的访问。
多个进程可以将同一个文件映射到它们的地址空间从而实现共享内存。另外 XSI 共享内存不是使用文件,而是使用内存的匿名段。
6. 套接字
与其它通信机制不同的是,它可用于不同机器间的进程通信。
死锁
必要条件
1. 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
2. 不可抢占:已经分配给一个进程的资源不能强制性地被抢占,它只能被占有它的进程显式地释放。
3. 占有和等待:已经得到了某个资源的进程可以再请求新的资源。
4. 环路等待:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。
处理策略
I. 鸵鸟策略
把头埋在沙子里,假装根本没发生问题。
因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。
当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。
大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
II. 死锁检测与死锁恢复
不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复
1. 每种类型一个资源的死锁检测
上图为资源分配图,其中方框表示资源,圆圈表示进程。资源指向进程表示该资源已经分配给该进程,进程指向资源表示进程请求获取该资源。
图 a 可以抽取出环,如图 b,它满足了环路等待条件,因此会发生死锁。
每种类型一个资源的死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。
2. 每种类型多个资源的死锁检测
有三个进程四个资源,每个数据代表的含义如下:
E 向量:资源总量
A 向量:资源剩余量
C 矩阵:每个进程所拥有的资源数量,每一行都代表一个进程拥有资源的数量
R 矩阵:每个进程请求的资源数量
进程 P1和 P2所请求的资源都得不到满足,只有进程 P3 可以,让 P3 执行,之后释放 P3 拥有的资源,此时 A = (2 2 2 0)。P2 可以执行,执行后释放 P2 拥有的资源,A = (4 2 2 1) 。P1 也可以执行。所有进程都可以顺利执行,没有死锁。
每个进程最开始时都不被标记,执行过程有可能被标记。当算法结束时,任何没有被标记的进程都是死锁进程。
算法总结如下:
1. 寻找一个没有标记的进程 P i ,它所请求的资源小于等于 A。
2. 如果找到了这样一个进程,那么将 C 矩阵的第 i 行向量加到 A 中,标记该进程,并转回 1
3. 如果没有这样一个进程,算法终止。
3. 死锁恢复
利用抢占恢复
利用回滚恢复
通过杀死进程恢复
III. 死锁预防
在程序运行之前预防发生死锁。
1. 破坏互斥条件
例如假脱机打印机技术允许若干个进程同时输出,唯一真正请求物理打印机的进程是打印机守护进程。
2. 破坏占有和等待条件
一种实现方式是规定所有进程在开始执行前请求所需要的全部资源。
3. 破坏不可抢占条件
给资源统一编号,进程只能按编号顺序来请求资源。
4. 破坏环路等待
给资源统一编号,进程只能按编号顺序来请求资源。
IV. 死锁避免
在程序运行时避免发生死锁。
1. 安全状态
图 a 的第二列 Has 表示已拥有的资源数,第三列 Max 表示总共需要的资源数,Free 表示还有可以使用的资源数。从图 a 开始出发,先让 B 拥有所需的所有资源(图 b),运行结束后释放 B,此时 Free 变为 5(图 c);接着以同样的方式运行 C 和 A,使得所有进程都能成功运行,因此可以称图 a 所示的状态时安全的。
定义:如果没有死锁发生,并且即使所有进程突然请求对资源的最大需求,也仍然存在某种调度次序能够使得每一个进程运行完毕,则称该状态是安全的。
安全状态的检测与死锁的检测类似,因为安全状态必须要求不能发生死锁。下面的银行家算法与死锁检测算法非常类似,可以结合着做参考对比。
2. 单个资源的银行家算法
一个小城镇的银行家,他向一群客户分别承诺了一定的贷款额度,算法要做的是判断对请求的满足是否会进入不安全状态,如果是,就拒绝请求;否则予以分配。
上图 c 为不安全状态,因此算法会拒绝之前的请求,从而避免进入图 c 中的状态。
3. 多个资源的银行家算法
上图中有五个进程,四个资源。左边的图表示已经分配的资源,右边的图表示还需要分配的资源。最右边的 E、P 以及 A 分别表示:总资源、已分配资源以及可用资源,注意这三个为向量,而不是具体数值,例如 A=(1020),表示 4 个资源分别还剩下 1/0/2/0。
检查一个状态是否安全的算法如下:
1. 查找右边的矩阵是否存在一行小于等于向量 A。如果不存在这样的行,那么系统将会发生死锁,状态是不安全的。
2. 假若找到这样一行,将该进程标记为终止,并将其已分配资源加到 A 中。
3. 重复以上两步,直到所有进程都标记为终止,则状态时安全的。
如果一个状态不是安全的,需要拒绝进入这个状态。
内存管理
虚拟内存
虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。
为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。这些页被映射到物理内存,但不需要映射到连续的物理内存,也不需要所有页都必须在物理内存中。当程序引用到不在物理内存中的页时,由硬件执行必要的映射,将缺失的部分装入物理内存并重新执行失败的指令。
从上面的描述中可以看出,虚拟内存允许程序不用将地址空间中的每一页都映射到物理内存,也就是说一个程序不需要全部调入内存就可以运行,这使得有限的内存运行大程序成为可能。例如有一台计算机可以产生 16 位地址,那么一个程序的地址空间范围是 0~64K。该计算机只有 32KB 的物理内存,虚拟内存技术允许该计算机运行一个 64K 大小的程序。
分页系统地址映射
内存管理单元(MMU)管理着地址空间和物理内存的转换,其中的页表(Page table)存储着页(程序地址空间)和页框(物理内存空间)的映射表。
一个虚拟地址分成两个部分,一部分存储页面号,一部分存储偏移量。
下图的页表存放着 16 个页,这 16 个页需要用 4 个比特位来进行索引定位。例如对于虚拟地址(0010 000000000100),前 4 位是存储页面号 2,读取表项内容为(110 1),页表项最后一位表示是否存在于内存中,1 表示存在。后 12 位存储偏移量。这个页对应的页框的地址为 (110 000000000100)。
页面置换算法
在程序运行过程中,如果要访问的页面不在内存中,就发生缺页中断从而将该页调入内存中。此时如果内存已无空闲空间,系统必须从内存中调出一个页面到磁盘对换区中来腾出空间。
页面置换算法的主要目标是使页面置换频率最低(也可以说缺页率最低)。
1. 最佳
OPT, Optimal replacement algorithm
所选择的被换出的页面将是最长时间内不再被访问,通常可以保证获得最低的缺页率。
是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
举例:
一个系统为某进程分配了三个物理块,并有如下页面引用序列:7,0,1,2,0,3,0,4,2,3,0,3,2,1,2,0,1,7,0,1
开始运行时,先将 7, 0, 1 三个页面装入内存。当进程要访问页面 2 时,产生缺页中断,会将页面 7 换出,因为页面 7 再次被访问的时间最长。
2. 最近最久未使用
LRU, Least Recently Used
虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。LRU 将最近最久未使用的页面换出。
为了实现 LRU,需要在内存中维护一个所有页面的链表。当一个页面被访问时,将这个页面移到链表表头。这样就能保证链表表尾的页面是最近最久未访问的。
因为每次访问都需要更新链表,因此这种方式实现的 LRU 代价很高。
3. 最近未使用
NRU, Not Recently Used
每个页面都有两个状态位:R 与 M,当页面被访问时设置页面的 R=1,当页面被修改时设置 M=1。其中 R 位会定时被清零。可以将页面分成以下四类:
R=0,M=0
R=0,M=1
R=1,M=0
R=1,M=1
当发生缺页中断时,NRU 算法随机地从类编号最小的非空类中挑选一个页面将它换出。
NRU 优先换出已经被修改的脏页面(R=0,M=1),而不是被频繁使用的干净页面(R=1,M=0)。
4. 先进先出
FIFO, First In First Out
选择换出的页面是最先进入的页面。
该算法会将那些经常被访问的页面换出,导致缺页率升高。
5. 第二次机会算法
FIFO 算法可能会把经常使用的页面置换出去,为了避免这一问题,对该算法做一个简单的修改:
当页面被访问 (读或写) 时设置该页面的 R 位为 1。需要替换的时候,检查最老页面的 R 位。如果 R 位是 0,那么这个页面既老又没有被使用,可以立刻置换掉;如果是 1,就将 R 位清 0,并把该页面放到链表的尾端,修改它的装入时间使它就像刚装入的一样,然后继续从链表的头部开始搜索。
6. 时钟
Clock
第二次机会算法需要在链表中移动页面,降低了效率。时钟算法使用环形链表将页面连接起来,再使用一个指针指向最老的页面。
分段
虚拟内存采用的是分页技术,也就是将地址空间划分成固定大小的页,每一页再与内存进行映射。
下图为一个编译器在编译过程中建立的多个表,有 4 个表是动态增长的,如果使用分页系统的一维地址空间,动态增长的特点会导致覆盖问题的出现。
分段的做法是把每个表分成段,一个段构成一个独立的地址空间。每个段的长度可以不同,并且可以动态增长。
段页式
程序的地址空间划分成多个拥有独立地址空间的段,每个段上的地址空间划分成大小相同的页。这样既拥有分段系统的共享和保护,又拥有分页系统的虚拟内存功能。
分页与分段的比较
对程序员的透明性:分页透明,但是分段需要程序员显式划分每个段。
地址空间的维度:分页是一维地址空间,分段是二维的。
大小是否可以改变:页的大小不可变,段的大小可以动态改变。
出现的原因:分页主要用于实现虚拟内存,从而获得更大的地址空间;分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护。
磁盘调度
磁盘结构
盘面(Platter):一个磁盘有多个盘面;
磁道(Track):盘面上的圆形带状区域,一个盘面可以有多个磁道;
扇区(Track Sector):磁道上的一个弧段,一个磁道可以有多个扇区,它是最小的物理储存单位,目前主要有 512 bytes 与 4 K 两种大小;
磁头(Head):与盘面非常接近,能够将盘面上的磁场转换为电信号(读),或者将电信号转换为盘面的磁场(写);
制动手臂(Actuator arm):用于在磁道之间移动磁头;
主轴(Spindle):使整个盘面转动。
磁盘调度算法
读写一个磁盘块的时间的影响因素有:
1. 旋转时间(主轴转动盘面,使得磁头移动到适当的扇区上)
2. 寻道时间(制动手臂移动,使得磁头移动到适当的磁道上)
3. 实际的数据传输时间
其中,寻道时间最长,因此磁盘调度的主要目标是使磁盘的平均寻道时间最短。
1. 先来先服务
FCFS, First Come First Served
按照磁盘请求的顺序进行调度。
优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。
2. 最短寻道时间优先
SSTF, Shortest Seek Time First
优先调度与当前磁头所在磁道距离最近的磁道。
虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。
3. 电梯算法
SCAN
电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。
因为考虑了移动方向,因此所有的磁盘请求都会被满足,解决了 SSTF 的饥饿问题。
链接
编译系统
以下是一个 hello.c 程序:
#include <stdio.h> int main() { printf("hello, world\n"); return 0; }
在 Unix 系统上,由编译器把源文件转换为目标文件。
gcc -o hello hello.c
这个过程大致如下:
1. 预处理阶段:处理以 # 开头的预处理命令;
2. 编译阶段:翻译成汇编文件;
3. 汇编阶段:将汇编文件翻译成可重定位目标文件;
4. 链接阶段:将可重定位目标文件和 printf.o 等单独预编译好的目标文件进行合并,得到最终的可执行目标文件。
静态链接
静态链接器以一组可重定位目标文件为输入,生成一个完全链接的可执行目标文件作为输出。
链接器主要完成以下两个任务:
1. 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。
2. 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。
目标文件
可执行目标文件:可以直接在内存中执行;
可重定位目标文件:可与其它可重定位目标文件在链接阶段合并,创建一个可执行目标文件;
共享目标文件:这是一种特殊的可重定位目标文件,可以在运行时被动态加载进内存并链接;
动态链接
静态库有以下两个问题:
1. 当静态库更新时那么整个程序都要重新进行链接;
2. 对于 printf 这种标准函数库,如果每个程序都要有代码,这会极大浪费资源。
共享库是为了解决静态库的这两个问题而设计的,在 Linux 系统中通常用 .so 后缀来表示,Windows 系统上它们被称为 DLL。
它具有以下特点:
1. 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中;
2. 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。
操作系统
1. 操作系统概述
A. 基本概念
概念
各类观点
用户观点:为了使用户更好的进行单人工作
系统观点:操作系统是计算机的资源管理程序
进程观点:控制协调进程的运行
虚拟机观点:操作系统为用户提供更多服务功能和良好的工作环境
控制&管理软硬件,提供接口&环境的 最基本系统软件
特征
并发
并发concurrency
两个或多个事件宏观同时发生,微观交替发生
并行parallelism
两个或多个事件同一时刻发生
共享
并发执行的程序共同使用
互斥共享
摄像头
同时共享(宏观并发,微观交替)
发送文件
最基本
虚拟
时分复用
虚拟CPU
空分复用
虚拟存储器
异步
进程走走停停(原因:并发)
功能和目标
系统资源管理者
处理机管理
存储器管理
文件管理
设备管理
用户和系统间接口
缓冲管理指令由OS管理,对人透明,不属于X
命令接口
联机/交互式
命令解释器,shell属于命令接口的一部分
脱机/批处理
作业操作说明书
程序接口
一组系统调用组成,通过程序间接使用
图形接口
对硬件的拓展
裸机
扩充机器(虚拟机)
发展和分类
批处理
单道
单道、顺序、自动
多道
多道、宏观并行、微观串行
分时操作系统
同时、交互、独立、及时
实时操作系统
及时可靠
硬实时
软实时
其他
嵌入式
网络
集群
分布式
B. 运行机制和体系结构
运行机制
程序
OS内核程序
能执行特权指令,运行在核心态
进程切换发生
用户自编程序
用户态
指令instruction
处理器能识别、执行的命令
特权指令
不允许用户程序使用
如内存清零,IO指令,置中断指令,存取内存保护寄存器
非特权指令
例如普通运算指令
如何判断是否可以执行特权指令?
处理器状态
用户态(目态、PSW=0):非特权
核心态(管态、PSW=1):特权+非特权
管 to 目
一条修改PSW的特权指令
目 to 管
中断引起,硬件自动完成
中断是“夺回CPU控制权,变态并处理”的唯一途径,并发的基础
有了中断,单道批处理->多道批处理
内核
体系结构
大内核:计算机层次结构中内核下面两层
微内核:计算机层次结构图中内核最底层
C. 中断和异常
内中断(异常、例外、陷入)
不能屏蔽
信号来源
CPU内部
与当前执行指令有关
分类
自愿中断
指令中断
强迫中断
硬件故障(缺页)
非法操作码、地址越界、算数溢出
软件故障(x/0)
分类
陷阱
有意为之
故障
错误条件引起,可恢复
终止
不可恢复
外中断(狭义的中断)
信号来源
CPU外部
与当前执行指令无关
例如
外设请求
IO处理完成,希望CPU下一个IO请求,例如打印机完成任务后
时钟中断
一个固定的时间到,让CPU处理计时、启动定时运行的任务等
人工干预
用户终止进程
D. 系统调用
组成程序接口
避免各个进程随意使用系统资源,统一通过系统调用进行管理
执行陷入/访管/trap指令
把CPU使用权主动交给OS内核程序
称为访管中断
发生在用户态,但被调用程序在核心态执行
高级语言:应用程序->库函数->系统调用
2. 进程管理
A. 进程、线程概念&区别
B. PCB、进程状态与转换
C. 进程同步概念
a. 实现临界区互斥方法
b. 信号量机制及PV操作
c. 经典同步问题(使用信号量机制解决)
D. 进程间通信
a. 共享存储系统
b. 消息传递系统
c. 管道
E. 进程调度基本原则
典型调度算法
F. 死锁形成原因&必要条件
死锁预防
死锁避免
死锁检测与解除
3. 内存管理
A. 基本概念
a. 程序装入与链接
b. PA&VA
c. 重定位
d. 内存保护
B. 分区管理;交换&覆盖
C. 分配管理方式
D. 虚拟内存
a. 概念
b. 局部性原理
c. 缺页中断
d. 地址变换
E. 页面置换算法
4. 文件管理
A. 文件的逻辑结构
B. 文件的物理结构(非空闲空间管理)
C. 文件存储空间管理(空闲空间管理)
D. 文件
a. 组织方式
b. FCB
c. 目录结构
d. 文件存取控制
E. 文件系统
层次结构
F. 磁盘
a. 结构
b. 调度算法
c. 廉价冗余磁盘阵列
5. 设备管理
A. IO设备基础
什么是IO设备
鼠标键盘
显示器
移动硬盘
分类
块设备
如移动硬盘
特点
传输快
可寻址
字符设备
传输慢
不可寻址
常采用中断驱动的方式
B. IO控制方式
程序直接控制方式
轮询
CPU干预频率频繁
传送单位
字
数据流向
读
IO->CPU寄存器->内存
写
反过来
读写都需CPU寄存器
优缺点
优点
简单
缺点
轮询导致CPU忙等
中断驱动方式
程序直接控制方式中引入中断,异步
优缺点
优点
CPU和IO可以并行工作
缺点
频繁中断消耗较多CPU时间
直接存储器存取(DMA)方式(去掉数据流向中的CPU寄存器)
传送单位
块
数据流向
不再需要CPU寄存器
优缺点
优点
解放CPU,提高并行性
缺点
CPU一次指令只能读一个或多个连续的数据块,并且读入内存以后也必须是连续的,如果不连续需要多次中断处理
通道(硬件)
弱鸡版CPU
C. 软件层次结构
用户层软件
提供接口
设备独立性软件(与硬件无关的功能)
提供系统调用
对设备的保护
差错处理
设备的分配与回收
缓冲区管理
逻辑设备名到物理设备名的映射关系
设备驱动程序
负责对硬件设备的具体控制
中断处理程序
I/O完成后,控制器发送中断信号,系统根据中断信号类型选择相应的中断处理程序
硬件
D. 脱机技术SPOOLing
脱机技术
脱离主机的控制进行I/O
假脱机技术
软件模拟
将数据放在内存缓冲区
可以把物理设备虚拟成逻辑设备,实现设备共享
例如共享打印机
E. 缓冲区
充满才能取
空了才能放
半双工:一个缓冲区(管道)
全双工:两个缓冲区
进程管理
A. 进程、线程概念&区别
进程
定义
单道程序
计算机的所有资源,包括IO等都是单个程序的
多道程序
引入进程、进程实体的概念
系统需要记录每个程序运行到哪里,数据在哪里,分配了哪些系统资源
进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
动态性
进程映像是静态的
进程映像组成
程序段:代码
数据段:程序运行时使用的、产生的数据(变量,宏定义的常量)
PCB:操作系统管理程序运行所需信息
进程存在的唯一标志
创建/撤销进程=创建/撤销进程映像PCB
进程描述信息
PID
UID
进程控制+管理信息
资源分配清单
处理机信息
组织
链接
索引
特征
动态性
并发性
独立性:资源分配及调度的基本单位
异步性
结构性
线程
为什么要线程?
线程和进程对比
并发进程封闭性
执行速度不改变执行结果
线程不能创建进程,其他均可
dll库的系统线程,被不同进程调用,是同一线程
多线程模型(内核级线程和用户级线程)
多对一模型(用户级线程)
线程管理工作由应用程序负责
对用户不透明,对操作系统透明
一个用户级线程被阻塞时,整个进程都会被阻塞
只能单核运行
开销小、效率高
不需要切换核心态
一对多模型(内核级线程)
线程管理工作由操作系统负责
多核并行,并发强
管理成本高
需要切换到核心态
多对多
结合两者优点
B. PCB、进程状态与转换
进程的状态及其转换
五种状态
三种基本状态
运行态
就绪态
阻塞态
另外两种状态
创建态
终止态
只要就绪队列不空,CPU效率为100%
进程控制
定义
控制进程状态转换
原语
防止PCB修改内容和修改队列中间插入其他操作导致系统错误
核心态下的特权指令
功能
更新PCB信息
将PCB插入合适的队列
分配、回收资源
分类
创建
终止
阻塞+唤醒
切换
调度是决策,切换是执行
进程获得CPU是通过调度得到的
C. 进程同步概念
a. 实现临界区互斥方法
概念
什么是进程同步(直接制约关系,各进程需要直接合作)
异步性:并发执行的进程以各自独立的、不可预知的速度向前推进
进程同步机制:控制并发的程序运行的顺序
什么是进程互斥(间接制约关系)
资源共享方式
互斥:临界资源
同时
对于临界资源的访问,必须互斥的进行
四个部分
进入区(上锁)
临界区(访问临界资源)
退出区(解锁)
剩余区(其他)
原则
空闲让进
忙则等待
有限等待
让权等待(释放CPU,防止忙等待)
进程互斥实现方式(软件)
1. 单标志法
进程访问完临界区后权限转交
每个进程进入临界区的权限只能由另一个进程赋予
缺点
违背空闲让进
P0允许访问,但是P0一直不访问,则P1无法访问临界区(P1进入的权限只能由P0赋予)
2. 双标志先检查
boolean flag[n]标记各个进程想进入临界区的意愿
先检查,后设置flag[i]=true,进入临界区
缺点
违背忙则等待
检查和上锁不是一气呵成,导致两个进程同时进入临界区
3. 双标志后检查
先上锁,后检查
缺点
违反空闲让进,有限等待
4. Peterson算法
想用但谦让,后进行原子性检查flag[n] + turn
最后一个谦让的人失去机会
未遵循让权等待的原则
轮询,没有让出CPU
进程互斥实现方式(硬件)
中断屏蔽方法
利用开、关中断指令
过程
关中断(只对单核有用)
临界区
开中断(只对单核有用)
不适用于多处理机
只适用于内核进程(开关中断只能在内核态运行)
简单高效
硬件指令方法
硬件实现,执行过程不允许中断
TestAndSet
Swap指令
保证了检查和上锁的原子性
适用于多处理机
不能满足让权等待(while(TestAndSet(&lock));)
b. 信号量机制及PV操作
使用操作系统提供的一对原语来对信号量进行操作
信号量
实质
变量
表示
系统资源数量
原语
实现方式
使用硬件方式
wait(S) = P(S)
signal(S) = V(S)
分类
整型信号量
操作
初始化
P(S)
x.wait()
使用打印机资源
V(S)
x.signal()
不满足让权等待,会发生忙等
记录型信号量(解决忙等)
上锁
运行to阻塞
释放
阻塞to运行
核心:自我阻塞
信号量机制实现进程互斥
互斥信号量mutex
初始值=1
信号量机制实现进程同步
同步信号量semaphore
初始值=0
信号量机制实现前驱关系
每一对前驱关系都是一个进程同步问题
设置多个s=0
c. 经典同步问题(使用信号量机制解决)
做题步骤
1. 确定资源
a,b,c,d
同时确定资源的初始值
互斥为1
同步初值由用户确定
消息已存在时,>0
为了确保资源的特殊关系构造变量,如count
count=0
释放资源
count>=1
占有资源
需要有mutex保护
带有下划线的资源表示需要 x_mutex 保护,保证互斥访问
mutex的初值为1
2. 确定对象
A,B,C,D
3. 对每个对象进行步骤分析
相继的步骤(无条件)合二为一
C
获取号码
empty
full
等待叫号+获取服务
service
S
叫号+为客户服务
empty, service
full
写在前面的信号量先占用/释放
4. 对每个步骤分析IO
IO相同的构成互斥关系
IO不同的构成同步关系
5. 去除冗余资源
去掉后依然不会同时访问该资源
互斥关系常常可以省略
6. 按照关系对每个对象写代码
生产者消费者问题
单生产者+单消费者
生产者
生产商品
放到缓冲区
empty
full
消费者
在缓冲区取出
full
empty
使用商品
生产,消费的代码不要放在PV操作之间,否则降低并发度
多生产者+多消费者
爸爸
削苹果
放苹果
plate
apple
妈妈
剥橘子
放橘子
plate
orange
儿子
拿橘子
orange
plate
吃橘子
女儿
拿苹果
apple
plate
吃苹果
吸烟者问题
读者写者问题
要求
读者并行,写者独占
实现方式
添加count变量,记录读进程
第一个读进程加锁
if(count==0)P(rw); count++;
最后一个读进程退出时解锁
count--; if(count==0)V(rw);
一个rw=1实现文件互斥
一个mutex实现读进程加锁和count++,count--和解锁一气呵成
上述算法问题
饥饿
只要有读进程,写进程就需要一直等待
解决
w=1用于解决写进程饥饿
在写进程整体和读进程加锁外围加上P(w)和V(w)
V(w)时唤醒的是队首进程,不一定写者
不能实现写进程优先,而是先来先服务
读写公平法
实现
读者
请求读
w, (count++=0?)rw
w
读
(--count=0?)rw
写者
请求写+写
w, rw
rw, w
哲学家进餐
重点
每个哲学家需要同时持有两个临界资源才可以执行
导致
可能产生死锁
如何防止死锁
最多四个哲学家吃饭
奇数哲学家优先左边筷子,偶数优先右边筷子
仅当哲学家左右两只筷子都可以使用时才可以拿起筷子
d. 管程
信号量机制问题
编写程序困难
易出错
思想
把对共享资源的操作封装起来
管程的定义和特征
类似于一个类
仅允许一个进程(互斥)执行/进入管程内某个内部过程(方法)
由编译器实现
管程中设置条件变量和wait、notify解决同步问题
Java中的synchronized
同一时间只能被一个线程调用
组成
(局限于管程)共享数据结构(及其说明)
(对管程内数据结构)操作的一组过程
(对管程内数据结构)设置初值的语句
管程外过程调用管程内数据结构的说明
条件变量
x.wait()
将自己插入x条件的等待队列,并释放管程
不包含对x条件的判断
if(S<=0) x.wait();
跟wait(X) 不一样
x.signal()
唤醒一个因x条件而阻塞的进程
D. 进程间通信
原因
各个进程的内存地址空间相互独立
方式
共享储存
两个进程对共享空间访问必须是互斥的(P、V操作)
分类
基于数据结构
低级通信方式
效率低
对数据格式有限制
基于存储区
数据的形式、存放位置由进程控制
操作系统只在内存中划出一块共享区域
高级通信方式
效率高
消息传递
格式化消息
消息头+消息体
分类
直接通信
消息->接收进程的消息缓冲队列
间接通信
消息->信箱->接收进程
管道
连接读写进程的一个共享文件
本质
内存中一个大小固定的缓冲区
协调条件
互斥
同步
确定对方存在
半双工
同一时间内只能单向传输
如果要全双工:两个管道
工作流程
管道写满:写进程的write()系统调用将被阻塞
管道变空:读进程的read()系统调用将被阻塞
没写满不允许读,没读空不允许写
数据读取时被抛弃,所以只能有一个读进程
E. 进程调度基本原则
处理机调度
调度的三个层次
高级调度
是否创建进程
作业从后备队列挑选,分配内存,IO设备,并建立进程
每个作业只能调入/调出1次
中级调度
是否进入内存
引入虚拟存储技术、提高内存利用率和系统吞吐量
外存和内存之间的调度
to挂起状态
暂时调到外存等待的进程状态
PCB常驻内存(挂起时仍需对进程进行管理,管理信息在PCB中)
分类(七状态模型)
就绪挂起
阻塞挂起
to就绪态
具备允许条件的重新调入内存
低级调度(进程调度)
是否分配CPU
就绪->运行
高频
进程调度(低级调度)的时机、切换
进程调度的时机
需要进行
主动放弃
被动放弃
不能进行
1. 处理中断的过程中
2. 进程在OS内核程序临界区中
临界资源
各个进程互斥访问的资源
例如打印机打印中,临界资源不会解锁,此时如果不进行进程调度,则CPU空闲
内核程序临界区
用来访问内核数据结构,例如进程的就绪队列
例如进程A在访问就绪队列的内核程序临界区,则其会对就绪队列上锁,在退出临界区以前,其他程序无法访问就绪队列,也就无法完成进程调度
3. 原语
进程调度的方式
非抢占式
开销小,但是无法及时处理紧急任务,适合批处理系统
抢占式
分时、实时操作系统
广义的进程调度
狭义的进程调度
就绪队列中选择要运行的进程
进程切换
一个进程让出处理机,另一个进程占用处理机
步骤
保存原来运行进程的数据
恢复新的运行进程的数据
调度算法的评价指标
1. CPU利用率
2. 系统吞吐量=作业数/时间
3. 周转时间
提交到完成的时间
作业等待
就绪排队
处理机允许
IO操作
平均·
带权·
周转/实际运行 >=1
平均带权·
4. 等待时间
作业
建立进程后等待时间 + 建立进程前在外存中后备队列等待时间
进程
进程建立后的等待时间,IO算作服务,不计入等待时间
5. 响应时间
调度算法
批处理系统
先来先服务FCFS
算法思想
公平
规则
先来先服务
作业调度还是进程调度
作业调度:哪个作业先到达后备队列
进程调度:哪个进程先到达就绪队列
优缺点
简单,公平
带权周转时间特别大,对长作业有利,对短作业不利
短作业优先SJF
算法思想
追求较少平均等待时间,平均周转时间,平均带权周转时间
作业调度还是进程调度
作业SJF,短作业优先
进程SPF,短进程优先
改成抢占式
SRTN最短剩余时间优先
优缺点
对短作业有利,长作业不利
未考虑作业紧迫程度
估计时间而定,不可靠
高响应比优先HRRN
算法思想
响应比优先
规则
响应比=(等待时间+要求服务时间)/(要求服务时间)
作业调度还是进程调度
既可用于作业调度,也可用于进程调度
优缺点
综合前两者
交互式/分时系统
优先级调度
算法思想
实时操作系统需要处理紧急任务
算法规则
优先级
系统进程优先
前台进程优先
I/O繁忙型进程优先
作业调度还是进程调度
作业调度+进程调度+I/O调度
优缺点
灵活
饥饿
时间片轮转RR
算法思想
公平
规则
轮流
作业调度还是进程调度
进程调度:时间片是CPU的时间片,只有建立了进程后才能被分配时间片
优缺点
公平
响应快
切换有开销
不区分任务的紧急程度
多级反馈队列
算法思想
折中
算法规则
多级队列
优先级从高到低
时间片从小到大
FCFS,用完时间片后放入下一级队列队尾
作业调度还是进程调度
进程调度
优缺点
相对公平(FCFS)
响应快(RR)
短进程快(SPF)
不必估计运行时间(避免作假)
灵活调整各类偏好(IO阻塞进程重放,保持优先级)
F. 死锁形成原因&必要条件
预防
破坏死锁的必要条件
1. 互斥
例如SPOOLing技术,将独占设备变成共享设备
2. 不剥夺
方案
方案一、请求资源不能得到满足时,释放所有资源(饥饿问题)
方案二、按优先级强制剥夺
缺点
1. 实现复杂
2. 可能导致前一阶段工作失效,一般只能用于易保存易恢复的资源,如CPU
3. 反复申请释放资源,降低系统吞吐量
4. 方案一饥饿
3. 请求和保持
方案
一次性申请
缺点
系统资源严重浪费
对资源需求大的进程饥饿
4. 循环等待
死锁的必要条件
循环等待不一定死锁
可能有其他进程能提供某资源,从而打破循环等待
方案
顺序资源分配法:按编号从小到大分配资源
缺点
不方便新增设备
资源浪费
编程麻烦
死锁
多进程因竞争资源造成僵局(互相等待),无外力将无法推进
避免
防止系统进入不安全状态(银行家算法)
安全状态
存在安全序列
系统以这种序列分配资源,则每个进程都能顺利完成
可能有多个
不安全状态
分配了一个资源后,找不出一个安全序列
有可能死锁
有可能回到安全状态
数据结构
Available: m
Max: m*n
Allocation: m*n
Need=Max-Allocation
Requesti:m
Pi提出的请求向量,需<=Needi
Requesti<=Needi?
Request<=Available?
尝试分配,修改Available, Allocation
运行安全性算法,是否有安全序列
完成
恢复Available, Allocation
等待
出错
检测和解除
死锁检测
资源分配图
两种节点
进程节点
资源节点,代表一类资源
两种边
请求边:进程指向资源
分配边
可完全简化=没有死锁发生(其实就是找到了一个安全序列,系统处于安全状态)
消除不掉的边连接所有死锁进程
死锁解除
资源剥夺法
挂起死锁进程,抢占资源
撤销进程法
代价很大
进程回退法
回退到可以避免死锁的点
操作系统需要记录进程历史信息
内存
A. 基本概念
a. 程序装入与链接
链接方式(形成完整的VA)
1. 静态链接
完整的可执行程序
2. 装入时链接
边装入边链接
3. 运行时动态链接
执行中需要模块才进行,便于共享
装入方式(形成PA)
1. 绝对装入(单道)
只适用于单道程序环境
2. 静态重定位(早期多道批处理)
装入程序负责逻辑地址->物理地址,直接加
装入时分配全部内存空间
运行期间不能移动
3. 动态重定位
动态运行时装入
运行时进行重定位
b. PA&VA
什么是内存
CPU<-内存<-外存
内存/物理地址
每个地址对应一个存储单元
一个存储单元由计算机按什么编址
逻辑地址
相对位置
内存管理的概念
内存空间的分配与回收
内存空间的虚拟(逻辑)扩充
c. 地址转换/重定位(逻辑->物理)
d. 内存保护(各自在自己的内存空间工作,互不干扰)
方法一、CPU中设置一对上下限寄存器
不是每道进程一个
方法二、采用重定位寄存器(start)+界地址寄存器(length)
B. 分区管理;交换&覆盖
内存空间的扩充
覆盖(淘汰)
解决
程序大小超过物理内存综合
思想
程序分段
常用段常驻内存的固定区
不常用的段放在内存覆盖区
按照自身逻辑结构,让那些不可能同时被访问的程序共享同一个覆盖区
缺点
必须由程序员声明,对用户不透明
同时运行的程序代码量大于主存,仍不能运行
同一个程序或者进程中
交换
思想
内存空间紧张时,将内存中某些进程暂时换出外存
PCB常驻内存,进程挂在挂起队列
实现
1. 快速磁盘——对换区
采用连续分配,提高速度
2. 换出:必须处于空闲状态
阻塞、优先级低
PCB不会、IO操作进程不会
3. 在许多进程运行且空间吃紧时启动,负荷降低暂停
不同进程和作业之间
中级调度
虚拟存储
C. 分配管理方式
连续分配
单一连续分配
系统区
用户区
简单、无外碎片,可以用覆盖
只能用于单用户、单任务OS;有内碎片,存储器利用率极低
固定分区分配
分区大小相等
分区大小不等
简单、无外碎片,可以用覆盖
程序可能太大无法放在一个分区
主存利用率极低
动态分区分配(外部碎片)
系统使用什么数据结构记录内存的使用情况
空闲分区表
空闲分区链
四种动态分区分配算法
首次适应(最好)
从低地址顺序查找
不需要对空闲分区链/表进行重新排列
首次适应始终从低地址开始查找,导致低地址部分出现很多小的空闲分区,大部分查找都会跳过它们(增加查找开销)
最佳适应
按容量由小到大查找
需要把分区链从小到大排列
缺点
每次分配几乎都会产生很小的外部碎片
所以这种算法会有很多的外部碎片
最坏适应算法
按容量由大到小
缺点
“大进程”无处安放
临近适应算法
临近适应算法改为从上次查找结束的位置开始查找,降低查找的开销
不需要对空闲分区链/表进行重新排列
高地址部分的大分区容易被使用,大进程无处安放
分配和回收的过程
外部碎片通过紧凑来解决
非连续分配
基本分页管理
地址是一维的
如何实现地址转换
页号
页面在内存中起始地址
页表
每个进程一个页表
页表每行为一个页表项
页表项=页号(逻辑地址)+块号(物理地址)
进程每一页对应一个页表项
每个页表在内存中连续存放
页号=index,不需要显示表示
页内偏移量
逻辑地址=concat(页号,页内偏移量)
基本地址变换机构
页表寄存器
两次访问内存
页表
每个进程的页表必须驻留内存
访问目标内存单元
具有快表的地址变换机构
局部性原理
时间局部性
最近访问过,不久后它可能再次被访问
空间局部性
访问了某个存储单元,不久后它附近的也可能会被访问
快表TLB
高速缓冲存储器
快表命中的话,只需一次访问内存+一次访问告诉缓冲存储器
两级页表
单级页表的问题
所有页表项都需要连续存放,需要较大的连续空间
没有必要让所有页表项都常驻内存(局部性原理)
解决
问题一:套娃
页目录表+非连续存放的页表
问题二:虚拟存储技术
不在内存中,产生缺页中断,调入内存
每个进程的地址空间、页目录、和PDBR(页目录基址)内容一一对应
基本分段存储管理方式
段
按程序自身逻辑划分
分配规则
每个段各自连续,各段可以不相邻
优点
编程方便,程序可读性更高
共享和保护
段S被共享时
P1与P2共享的是 S在共享段表的段表项
这NTM是什么鬼?
P1与P2中相应段表项指向S的同一个物理副本
S在P1与P2段表中有相同段号
动态链接和增长
段表
地址是二维的
段号+段内地址
段页式管理
段号+页号+页内偏移量
地址是二维的
段号+段内地址
分段、分页对比
分页
系统需求,对用户不可见
页的大小固定
地址是一维的
分段
更好的满足用户需求,对用户可见,用户编程需要给出段名
段的长度取决于用户的程序
地址是二维的
更容易实现信息的共享和保护
外部碎片
采用分页或分段后,提供用户的物理地址空间 = 总大小 - 段/页表长
不能确定
D. 虚拟内存
a. 概念
内存空间的扩充方式
覆盖
交换
虚拟存储
传统存储管理方式缺点
一次性
作业必须一次性装入内存才能运行
导致
大作业无法运行
并发度低
驻留性
装入后一直驻留直到结束
特征
多次性
多次装入
对换性
无需一直常驻,可换入换出
虚拟性
从逻辑上扩充了内存
必然采用离散分配
请求分页
请求分段
请求段页式
需要硬件支持
1. 内存+外存
2. 页表机制
3. 中断机构
4. 地址变换机构
b. 局部性原理
局部性原理(时间、空间)+高速缓存
c. 缺页中断
请求分页
新增功能
请求调页、段功能
页面、段置换功能
页表新增字段
状态位
是否在内存中
访问字段
换出时供参考
修改位
没有修改的不需要写回外存
外存地址
缺页中断机构(内中断的故障)
缺页的进程阻塞
d. 地址变换
E. 页面置换算法
1. 最佳(OPT)
以后永久不被使用或者最长时间不再使用的页面
无法实现,用于衡量别的算法
2. 先进先出(FIFO)
Belady异常
为进程分配的物理块数增大,缺页次数不降反增
算法性能差
最先进入的通常最容易被访问
3. 最近最少使用(LRU)
需要专门的硬件支持,性能好,但是实现困难开销大
4. 时钟(CLOCK)
(使用位/机会u,修改位m)
1. (0,0)
2. (0,1),对跳过的帧置u=0
3. (0,0)
4. (0,1)
5. 改进型的时钟
未修改的不用写回外存
6. 工作集模型
F. 页面分配策略
驻留集
请求分页存储管理系统中
给进程分配的物理块的集合
如果
驻留集太小:频繁缺页
太大:并发度下降
分配
固定
不存在固定分配全局置换,物理块数发生变化
局部置换
只能选择进程自己的物理块进行置换
可变
局部置换
根据缺页的频率动态增加或者减少物理块
全局置换
只要缺页就分配新的物理块
何时调入页面
预调页
请求调页:每次只能调入一页,I/O开销较大
抖动
现象
刚换出,马上又换入
刚换入,马上又换出
原因
进程分配的物理块太少
判断合适的物理块数
工作集:δt实际访问的页面集合
监测工作集,驻留集>=工作集
文件管理
A. 文件的逻辑结构
无结构文件(流式文件)
txt
有结构文件(记录式文件)
一般有关键字
分为
定长记录
可变长记录
有结构文件的逻辑结构
顺序文件
逻辑上
一个接一个
定长或者变长
物理上
顺序存储
链式存储
按关键字排列分类
串结构
记录之间的顺序与关键字无关
一般是按存入时间顺序
顺序结构
记录之间的顺序按关键字顺序排列
索引文件
解决
可变长记录快速查找
实现
索引表本身是定长记录的顺序文件
通过索引表可以实现快速查找第i个
如果索引按关键字排序,则可以快速查找
可以建立多个索引
缺点
修改记录时同时需要修改索引
索引文件可能会很大
用途
及时性要求比较高的场合
索引顺序文件
解决
索引文件过大
实现
对记录进行分组
对每个分组建立索引
索引项不需要按关键字顺序排列,方便插入
多级索引顺序文件
B. 文件的物理结构(非空闲空间管理)
很多操作系统中磁盘块大小和内存块、页面的大小相同
文件的逻辑地址
逻辑块号
块内地址
操作系统需要做的
逻辑块号与物理块号的映射
分类
连续分配
每个文件占有一组连续的块
支持
顺序访问
随机访问
缺点
文件拓展不方便
碎片难以利用(紧凑处理碎片,时间代价)
链接分配
隐式
不支持随机访问
方便拓展
没有碎片
显示
用一张表存放所有块的下一个块(FAT)
表存放在内存中
支持随机访问(FAT从磁盘读取到内存中,只需要两次磁盘读取即可随机访问)
文件分配表需要占用一定的存储空间
索引分配
索引表(逻辑块号<->物理块号)
逻辑块号是隐含的=index
支持随机访问
可扩展
索引表需要占用一定的存储空间
如果一个索引表超过了一个磁盘块
链接
多层
缺点
k层索引,小文件仍需k+1次读磁盘
混合
索引里包括直接地址和间接索引
直接地址直接指向数据块
间接索引指向下一级索引表
优点:
访问小文件所需的读磁盘次数减少
C. 文件存储空间管理(空闲空间管理)
存储空间的划分与初始化
文件卷(逻辑卷)
文件卷=一个分区
支持超大型文件的系统可以合并多个磁盘组成一个文件卷
目录区与文件区
每个分区
目录区
逻辑区
管理方法
空闲表
适用于连续分配
算法
首次适应
最佳适应
最坏适应
回收时注意合并
空闲链表
空闲盘块链
链接空闲的磁盘块
空闲盘区链
链接空闲的整块盘区
位示图法
用位表记录所有盘块
成组链接法
适用于大型文件系统
多层
D. 文件
a. 组织方式
文件基础
文件有哪些属性
文件名
同一目录下不允许重名文件
标识符
唯一
类型
位置
大小
创建时间
权限
修改时间
所有者信息
文件内部怎么组织
无结构文件(流式文件)
txt
记录式文件
excel
文件之间怎么组织
树状
操作系统需要提供哪些功能
创建
读取
写入
删除
打开
关闭
文件怎么存放
b. FCB
目录文件的一条记录
操作
搜索
创建文件
删除文件
显示目录
修改目录
c. 目录结构
目录结构
单级目录
不允许重名
两级目录
多用户,每个用户内部不能同名
多级目录
缺点:不便于实现文件共享
无环图目录
user1的demo和user2的myDemo是同一个文件
索引结点(对文件控制块的优化)
搜索时只关心文件名
将其他信息放在索引结点中
减少查找时的I/O读取次数,大大提升查找的效率
d. 文件存取控制
文件的基本操作
创建
调用create
找到所需空间
创建目录项
删除
调用delete
找到目录项
回收占用的磁盘块
删除目录项
读
调用open
找到目录项,并核查权限
将目录项复制到内存中
打开文件表
系统的打开文件表
进程的打开文件表
指向系统打开文件表的项
写
调用close
进程的打开文件表相应表项删除
回收内存空间等资源
系统的打开计数器count减1,如果count==0则删除此项
打开
调用read(已经打开)
关闭
调用write(已经打开)
文件共享
基于索引结点(硬链接)
不同目录的目录项指向同一个索引结点
目录->索引结点->文件
基于符号链(软链接)
目录->索引结点->快捷方式->文件目录
文件保护
口令保护
存放在系统中
优点
开销小
缺点
不安全
加密保护
访问文件必须解密,不需要存密码
优点
安全
缺点
消耗大
访问控制
在FCB中或索引结点中增加一个访问控制列表
E. 文件系统
层次结构
F. 磁盘
a. 结构
构成
磁盘
磁道
扇区
磁盘的物理地址
柱面号
盘面号
扇区号
b. 调度算法
一次磁盘读写操作时间
寻道时间
延迟时间
传输时间
算法
FCFS
寻道时间很长
最短寻找时间优先
每次寻道时间最短
性能好
饥饿
扫描算法
扫描到最外侧或最内侧才能改变方向
解决饥饿
多余的移动
对于各个磁道响应频率不平均
LOOK算法(SCAN多余的移动)
如果继续移动没有请求可以立即改变方向
没有多余的移动
C-SCAN算法(SCAN对于各个磁道响应频率不平均)
返回时不进行处理
还是有多余的移动
C-LOOK
前两者综合
c. 廉价冗余磁盘阵列
d. 减少磁盘延迟的方法
交替编号
由于读完一个扇区需要处理数据
所以读下一个扇区时,可能错过一部分数据
将逻辑相邻的扇区在物理上有一定间隔
错位命名
e. 磁盘管理
磁盘初始化
引导块
坏块的管理