操作系统强化小结
1. 概述
1.1 几种操作系统
批处理:1.单道批处理:内存中只有一道程序、一批程序自动执行、程序之间串行执行、无需人工干预;2. 多道批处理:内存中有多道程序、多个程序交替运行、轮流占用CPU、无法人机交互。
分时操作系统:将CPU时间划分为时间片分配给不同程序,实现了人机交互,用户可在很短时间内获得响应。
1.2 CPU的工作模式
CPU模式位(PSW):核心态、用户态
特权指令:CPU在内核态才能运行的指令
1.3 OS内核
基本功能:时钟管理、中断机制、原语
资源管理:进程管理、存储管理、文件管理
1.4 系统调用
系统调用提供了用户程序和OS内核之间的接口,应用程序通过系统调用来获得OS的服务
;以read()函数为例
;准备函数参数
mov rdi,fd ;文件描述符
mov rsi,buf ;缓冲区指针
mov rdx,nbytes ;要读取的字节数
;设置系统调用号
mov rax,0 ;sys_read的系统调用号是0
;触发系统调用
syscall
;处理返回值
mov [buffer],rax ;将返回值(读取的字节数)存储或处理
用户态 -> 内核态:用户程序发起系统调用(系统调用本质就是中断);发生中断,必须陷入内核态由OS内核处理中断;发生异常,必须陷入内核态由OS内核处理中断;用户程序试图执行特权指令。
内核态 -> 用户态:设置程序状态字寄存器PSW中的CPU模式位,将其置1
1.5 OS引导
主引导记录MBR:位于磁盘的0号扇区、磁盘引导程序、分区表信息
分区引导记录PBR:位于系统分区的0号扇区、分区引导程序、操作系统引导程序
流程:1.计算机启动,电源自检;2.计算机主板ROM区的BIOS程序被激活,负责初始化硬件,进一步自检;3.BIOS读取硬盘0号扇区的主引导记录MBR,启动磁盘引导程序;4.利用磁盘引导程序找到系统分区,在系统分区的0号扇区(PBR)启动分区引导程序,再由分区引导程序启动操作系统引导程序,得知OS内核位置并将其加载入主存。
1.6 虚拟机
1型虚拟机管理程序:1型VMM(Virtual Machine Manager)是唯一一个运行在内核态的OS。客户OS(虚拟机上的OS)以为自己运行在内核态,这种内核态叫虚拟化内核态,介于真正内核态和用户态之间。虚拟机上的用户程序运行在用户态,这种用户态被称为虚拟化用户态,他基本上就是用户态。
2型虚拟机管理程序:常用的VMware Workstation、Parallel desktop就是2型VMM,运行在两类虚拟机管理程序上的操作系统叫做客户操作系统,运行在底层硬件上的操作系统叫做宿主操作系统。
2. PV专题
2.1 模板
- 同步关系:
进程A | 进程B
操作 | P(同步信号量)
V(同步信号量) | 操作
- 互斥关系
进程A | 进程B
P(互斥信号量) | P(互斥信号量)
临界区 | 临界区
V(互斥信号量) | V(互斥信号量)
2.2 生产者-消费者
semaphore mutex = 1;
semaphore empty = n;
semaphore full = 0; //初始缓冲区为空
producer(){
while(1){
生产;
P(empty);
P(mutex);
放入缓冲区;
V(mutex);
V(full);
}
}
consumer(){
while(1){
P(full);
P(mutex);
从缓冲区取产品;
V(mutex);
V(empty);
消费;
}
}
互斥关系:缓冲区必须互斥访问,mutex=1
同步关系:先生产,再消费,empty=n,full=0
2.3 读者-写者(互斥问题)
规定:1. 允许多个读者可以同时对文件进行读操作;2. 只允许一个写者往文件中写信息;3. 任意一个写者在完成写操作之前不允许其他读者或写者工作;4. 写者执行写操作前应让已有读者和写者全部退出。
互斥关系:1. 写者和读者写者都互斥,rw=1;2. 读者之间不互斥,但读者走完了才能让写者进入,所以需要计数器count来记录读者数量,count不必是信号量,因此不需要排队,也不限制读者上限,count=0,对count的访问必须互斥,否则可能有多个读者同时修改count,造成结果错误,mutex=1;3. 相对写优先,w=1。
semaphore mutex = 1; //用来实现互斥访问
semaphore rw = 1; //实现写者和其他人的互斥
int count = 0; //记录一下当前读者数量,不为0就不准写者进
writer(){
while(1){
P(rw);
writing;
V(rw);
}
}
reader(){
while(1){
P(mutex);
if(count == 0){
P(rw);
}
count++;
V(mutex);
reading;
P(mutex);
count--;
if(count == 0){
V(rw);
}
V(mutex);
}
}
//相对写优先
semaphore mutex = 1; //用来实现互斥访问count
semaphore rw = 1; //实现写者和其他人的互斥
int count = 0; //记录一下当前读者数量,不为0就不准写者进
semaphore w = 1; //实现写者公平排队
writer(){
while(1){
P(w);
P(rw);
writing;
V(rw);
V(w);
}
}
reader(){
while(1){
P(w);
P(mutex);
if(count == 0){
P(rw);
}
count++;
V(mutex);
V(w);
reading;
P(mutex);
count--;
if(count == 0){
V(rw);
}
V(mutex);
}
}
2.4 哲学家用餐(解决死锁问题)
//方法一
semaphore count = 4; //限制只有n-1个哲学家就餐
semaphore chopsticks[5] = {1,1,1,1,1};
philosopher(){ //第i个哲学家
while(1){
P(count); //来得慢的哲学家暂时吃不到饭
P(chopsticks[i]); //先拿左边筷子
P(chopsticks[(i+1)%5]); //再拿右边筷子
eat;
V(chopsticks[i]); //放下左边筷子
V(chopsticks[(i+1)%5]); //放下右边筷子
V(count); //就餐结束
think;
}
}
//方法二
semaphore chopsticks[5] = {1,1,1,1,1}; //初始化五根筷子
semaphore mutex = 1; //筷子同时可用时才会拿
Pi(){
while(1){
P(mutex); //取筷子之前P互斥信号量,取不到筷子后面人也别取了
P(chopsticks[i]); //取左边筷子
P(chopsticks[(i+1)%5]); //取右边筷子
V(mutex);
eat;
V(chopsticks[i]); //放下左边筷子
V(chopsticks[(i+1)%5]); //放下右边筷子
think;
}
}
//方法三
semaphore chopsticks[5] = {1,1,1,1,1};
philosopher(){ //第i个哲学家
while(1){
if(i%2 == 0){
P(chopsticks[(i+1)%5]); //偶数位的哲学家拿右边的筷子
P(chipsticks[i]);
eat;
V(chopsticks[i]);
V(chopsticks[(i+1)%5]);
}
if(i%2 == 1){
P(chopsticks[i]); //奇数位的哲学家拿左边的筷子
P(chipsticks[(i+1)%5]);
eat;
V(chopsticks[i]);
V(chopsticks[(i+1)%5]);
}
}
}
3. 进程与线程
3.1 进程
3.1.1 进程实体
代码段、相关数据、PCB
PCB:1. 进程标识符:(1)外部标识符:为方便用户对进程的访问,为每个进程设置一个外部标识符,通常由字母和数字组成;(2)内部标识符:为方便系统对进程的使用,在OS中又为进程设置了内部标识符,赋予每个进程唯一一个数字标识符,通常是一个进程序号。2. 处理机状态:通用寄存器、PC、PSW、堆栈指针寄存器SP:指向用户自己堆栈的指针和指向内核栈的指针。3. 进程调度信息:(1)进程状态:指明进程当前的状态(阻塞、就绪、运行),作为进程调度、对换的依据;(2)进程优先级:描述进程使用处理机的优先级别(用一个整数表示),优先级高的优先获得处理机;(3)进程调度所需的其他信息:部分进程调度算法需要知道进程已等待CPU时间总和、进程已执行时间总和等;(4)事件:进程由执行状态转换为阻塞状态所等待发生的事件(阻塞的原因)。4. 进程控制信息:(1)程序和数据的地址:即程序和数据的内存或外存起始地址,便于该进程执行时能从PCB中快速找到程序和数据,页表的起始地址(物理地址);(2)进程同步和通信地址:比如消息队列指针、信号量等,它们可能会全部或部分放在PCB中;(3)资源清单:列出了该进程在运行期内所需的全部资源(除CPU外);(4)链接指针:给出本进程所在队列中的下一个进程的PCB的起始地址。
3.2 进程映像(建立在虚拟存储的基础上)
1.操作系统内核区:将操作系统内核映射到这个区域,区域内容对进程不可见;2. 共享库的存储映射区:将共享库(如动态链接库DLL)映射到进程的虚拟地址空间,使多个进程共享同一个物理内存副本的库,节省了内存资源,映射区域允许进程执行代码库中的代码,并访问库中的数据,就像这些代码和数据是进程自己的一部分一样;3. 代码段:程序的二进制代码,代码段是只读的,可被多个进程共享;4. 数据段:包括全局变量和静态变量;5. PCB:放在内核区;6. 堆:用来存放动态分配的变量,通过调用malloc函数动态地向高地址分配空间;7. 栈:用来实现函数调用,从用户空间的最大地址向低地址方向增长。
3.3 进程状态
就绪态:资源充分,只差CPU
阻塞态:主动阻塞,等待输入,将CPU让出
执行态
3.4 进程控制
1.创建:(1)过程:分配进程标识符,申请PCB,为进程分配资源,微程序和数据分配必要的内存,初始化PCB,若就绪队列未满则插入,父进程创建子进程,子进程可以继承父进程所拥有的资源(打开的文件,分配的缓冲区);(2)执行:父进程和子进程并发执行,父进程等待,直到其某个或全部子进程执行完毕;(3)内存映像:子进程是父进程的复制品(子进程与父进程具有相同程序和数据),子进程加载另一个新程序。2.终止:当子进程被撤销时,应将从父进程那里获得的资源归还父进程,在撤销父进程时,不一定同时撤销所有的子进程。3.阻塞:进程调用阻塞原语block将自己阻塞,是一种主动行为,OS介入,停止执行该进程,将PCB中进程状态改为阻塞态,将PCB插入阻塞队列,执行调度程序重新调度CPU。4.唤醒:被阻塞进程所期待的事件发生时,OS调用唤醒原语wakeup以将等待该事件的进程唤醒,OS将被阻塞的进程从等待该事件的阻塞队列中移出,将其PCB中的现行状态改为就绪,将该PCB插入就绪队列。
3.5 进程通信
共享存储:通信进程之间有一块用于通信的共享空间
消息传递:以格式化的消息为单位,将通信的数据封装在消息中
管道通信:“管道”指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,也叫pipe文件,管道不可以同时读写,管道同一时刻只能有一个方向的传输,管道满时写进程被阻塞,管道空时读进程被阻塞。
3.2 线程
3.2.1 基本概念
每个线程类似于独立的进程,只有一点区别:它们共享进程的地址空间,从而能够访问相同的数据;
线程只是作为独立调度和分配的单位,但是它们没有独立的地址空间,也没有掌握资源,完全依附于进程;
每个线程都有自己独立的栈和栈指针,并且线程之间没有做保护措施,每个线程的堆栈可以被其他线程读写或清除,由一个线程打开的文件也可以被其他线程读写。
3.2.2 实现方式
-
ULT用户级线程
把整个线程实现部分放在用户空间中,内核看到的就是一个单线程进程。
线程切换在用户空间实现,无需模式切换;
每个进程可以选择自己专用的调度算法,对自己内部的线程进行调度和管理;
ULT的实现和OS无关,因为ULT管理的代码会显式地出现在用户程序中,因此ULT甚至可以在不支持线程机制的OS平台上实现。
内核只识别到一个进程,因此调度的对象是整个用户进程,每次只能给整个进程分配CPU,进程中只有一个线程能执行,无法发挥多处理器系统优势;
进程中的某个线程发起IO系统调用,会导致进程中的所有线程全部被阻塞。 -
KLT内核级线程
内核可识别到内核级线程,因此调度的对象是内核级线程,在多处理机系统中,每次可以给不同的内核级线程分配CPU,提高并行度;
如果用户进程中的一个线程被阻塞,内核可以调度该进程内其他线程上处理机,或者调度其他进程的线程。
每次线程切换都需要CPU变态,开销较大;
TCB存放在内核空间中,内存占用较大。3.3 CPU调度
3.3.1 进程调度
1.原理:进程调度是CPU在内核态下完成的,进程调度基于中断机制。
2.方式:(1)非抢占调度方式:正在执行进程执行完毕或无法继续执行、正在执行的进程因提出IO请求而暂停执行、进程通信或同步过程中执行了某种原语操作(如block原语);(2)抢占调度方式:优先级高可抢占处理机;短进程优先原则;时间片原则,各进程按时间片运行,正在执行的进程的一个时间片用完后,停止该进程执行并重新分配CPU执行权。3.3.2 调度时机
主动放弃:运行完毕,正常终止;运行过程中发生异常;发生事件,请求阻塞。
被动放弃:进程时间片用完;有更高优先级进程进入就绪队列(抢占);有更加紧急的事情需要处理。
不可进程切换的情况:处理中断时;进程处于内核临界区时;执行原子操作时。3.3.3 调度目标
CPU利用率 = CPU有效工作时间/(CPU有效工作时间+CPU空闲等待时间)
系统吞吐量:单位时间内CPU完成作业的数量
周转时间 = 作业完成时间 - 作业提交时间
平均周转时间 = 作业周转时间之和/作业数量
带权周转时间 = 作业周转时间/作业实际运行时间
平均带权周转时间 = 带权周转时间之和/作业数量
等待时间:进程等待CPU时间之和
响应时间:从用户提交请求到系统首次产生响应所用时间3.3.4 调度算法
-
先来先服务FCFS
按照作业/进程到达的先后次序进行调度
只能是非抢占式
对长作业有利,对短作业不利
有利于CPU繁忙型作业/进程,不利于IO繁忙型作业/进程 -
短作业优先SJF(未说明就是非抢占式)
作业/进程越短,优先级越高,越先被调度
如果是抢占式SJF,作业/进程的剩余时间越短,优先级越高,越先被调度
平均等待时间和平均周转时间最优
对短作业有利,对长作业不利,甚至导致长作业饥饿
默认在估计运行时间相等时按照先后顺序服务 -
高响应比优先
相应比 = (等待时间+要求服务时间)/要求服务时间
每次调度时选择响应比最高的作业投入运行
克服了长作业饥饿问题 -
优先级调度
-
时间片轮转
所有就绪进程排成就绪队列,OS每隔一段时间就产生一次时钟中断,调度进程,将CPU分配给就绪队列首进程,令其执行一个时间片 -
多级队列调度
设置多个队列,将不同类型的进程分配到不同的就绪队列,每个队列实行不同的调度算法 -
多级反馈队列调度
结合了时间片调度和优先级调度算法,设置多个就绪队列,为每个队列赋予不同优先级;赋予各个队列的进程时间片不同,优先级越高的队列进程的时间片越小;每个队列采用FCFS算法;仅当1~i-1级队列为空,才调度i级,若CPU正在执行第i级队列中的某个进程时,有新进程进入优先级更高的队列,立即将正在执行的进程放回本级队列队尾,将CPU分配给新到的进程。
-
3.3 同步与互斥
3.3.1 同步机制准则
空闲让进:无进程处于临界区时,表明临界资源空闲,应允许一个请求进入临界区的进程立即进入自己的临界区,有效利用资源
忙则等待:已有进程进入临界区时,表明临界资源正在被访问,因而其他试图进入临界区的进程必须等待,以保证对临界资源的互斥访问
有限等待:对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,避免陷入“死等”状态
让权等待:进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态
3.3.2 软件
-
单标志法
//进程P0 while(turn!=0); //进入区 critical section; //临界区 turn = 1; //退出区 remainder section; //剩余区 //进程P1 while(turn!=1); //进入区 critical section; //临界区 turn = 0; //退出区 remainder section; //剩余区两个进程交替进入临界区
实现简单,但可能违背空闲让进 -
双标志先检查法
//进程P0 while(flag[1]); //进入区 flag[0] = true; //进入区 critical section; //临界区 flag[0] = false; //退出区 remainder section; //剩余区 //进程P1 while(flag[0]); //进入区 flag[1] = true; //进入区 critical section; //临界区 flag[1] = false; //退出区 remainder section; //剩余区每个进程访问临界区前,先检查临界区是否被访问,空闲才能进入
两个进程可能同时进入临界区,违背忙则等待 -
双标志后检查法
//进程P0 flag[0] = true; //进入区 while(flag[1]); //进入区 critical section; //临界区 flag[0] = false; //退出区 remainder section; //剩余区 //进程P1 flag[1] = true; //进入区 while(flag[0]); //进入区 critical section; //临界区 flag[1] = false; //退出区 remainder section; //剩余区先设置标志表示自己想要访问临界区,再检查对方标志,如果对方也想进入则等待,否则进入
双方互相谦让,导致饥饿 -
Peterson算法
//进程P0 flag[0] = true; //进入区 turn = 1; //进入区 while(flag[1] && turn==1); //进入区 critical section; //临界区 flag[0] = false; //退出区 remainder section; //剩余区 //进程P1 flag[1] = true; //进入区 turn = 0; //进入区 while(flag[0] && turn==0); //进入区 critical section; //临界区 flag[1] = false; //退出区 remainder section; //剩余区
解决了饥饿问题
3.3.3 硬件
-
TestAndSet指令
//test and set boolean TS(boolean *lock){ boolean old; old = *lock; *lock = true; return old; } do { ... while TS(&lock); critical section; lock = false; remainder section; }while(TRUE)TS可以看作一个执行过程不可分割的函数过程(原语)
使用TS管理临界区,要为每个临界区资源设置一个lock,初值为FALSE,表示临界资源空闲,进程进入临界区之前,先用TS测试lock,如果是FALSE,则可以进入,并将TRUE赋值给lock,关闭临界资源 -
Swap指令
//swap void swap(boolean *a,boolean *b){ boolean temp; temp = *a; *a = *b; *b = temp; } do{ key = TRUE; do{ swap(&lock,&key); }while(key!=FALSE); 临界区操作; lock = FALSE; ... }while(TRUE);为每个临界资源设置一个全局布尔变量lock,初值FALSE,利用上面的硬件指令完成进程互斥,但是这种方法也会造成访问进程不断进行测试,处于“忙等”状态,不符合“让权等待”原则
-
关中断
有进程在临界区执行期间,计算机系统关中断,从而不会引发调度,也就不会有进程或线程切换
3.3.4 管程
管程的名称
局部于管程内部的共享数据结构说明
对该数据结构进行操作的一组过程(或函数)
对局部于管程内部的共享数据结构设置初始值的语句
每次只允许一个进程在管程内执行某个内部过程
3.3.5 死锁
-
定义
一组进程中的每一个进程都在等待由该组进程中的其他进程才能引发的事件
饥饿:当等待时间(等CPU、等IO资源...)给进程的推进带来明显影响时,称发生了饥饿
饥饿不代表系统已经死锁,但至少有一进程执行被无限推迟 -
死锁产生的必要条件
(1)互斥条件:一段时间内,进程分配到的资源只能被自己占用,如果有其他进程此时请求该资源,则请求进程只能等待
(2)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但是对已有资源保持不放
(3)不可抢占条件:进程已获得的资源在未使用完之前不能被抢占,只能在进程使用完时由自己释放
(4)循环等待条件:发生死锁时必然存在一个进程----资源的循环链 -
死锁的处理策略
(1)死锁预防:通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个来预防产生死锁;循环不剥夺
(2)避免死锁:银行家算法;安全状态:如果系统能按某种进程推进顺序为每个进程分配其所需资源,满足每个进程对资源的最大需求,使每个进程都可顺利地完成,这样的进程推进顺序称为安全序列;不安全状态:如果找不到安全序列,那系统此时是不安全状态,进入不安全状态就可能发生死锁(也有可能不发生),若系统处于安全状态,系统便不会进入死锁状态
(3)检测死锁:资源分配图;进程画圆圈,资源画方框里的圆圈;若能把图中所有的边都消除,则称该图是可完全简化的,如果该图是不可完全简化的,则此时处于死锁状态
(4)解除死锁:资源剥夺法、撤销进程法、进程回退法
4. 内存管理
4.1 装入和链接
4.1.1 装入
绝对装入:事先知道程序将驻留在内存的什么位置,程序中的虚拟地址和实际物理地址完全相同,只适用于单道程序环境
可重定位装入(静态地址重定位):根据内存当前的情况,将装入模块装入到内存的适当位置,装入时对地址进行重定位,即完成虚拟地址到物理地址的映射,地址变换在装入时一次性完成,以后不再改变,因此程序运行时不能在内存中移动位置,也就是说静态重定位就是给没有对换功能的存储系统准备的,没有对换功能的内存管理方式采用静态重定位
动态运行时装入(动态地址重定位):在程序执行过程中进行地址变换,每次访问存储单元时才将虚拟地址转换为物理地址,为了使地址转换不影响指令的执行速度,需要重定位寄存器的支持,程序在内存中不必是连续的,一个程序可分为若干个目标模块,分别装入不相邻的存储区域,请求分页、动态分区分配(紧凑技术)采用动态重定位
4.1.2 链接
链接是将各种代码和数据片段收集并组合称为一个单一文件的过程,这个文件可被加载(复制)到内存并执行。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是程序被加载器加载到内存并执行时;甚至执行于运行时,也就是由应用程序来执行。在早期的计算机系统中,链接是手动执行的。在现代系统中,链接是由叫做链接器的程序自动执行的。
静态链接:在程序运行之前,先将各目标模块及它们所需的库函数,链接成一个完整的装入模块,以后不再拆开
装入时动态链接:将用户程序编译后所得到的一组目标模块,在装入内存时,采用边装入边链接的链接方式
运行时动态链接:在程序执行中根据需要动态地将库函数加载到内存中和目标模块进行链接
4.2 内存分配
4.2.1 连续分配
-
单一连续分配
单道程序环境、内存分为系统区和用户区、用户区被一道用户程序独占 -
固定分区分配
把整个用户空间分成几个固定大小区域,每个分区只装一个作业
分区使用表,按分区大小排队,各表项包括对应分区的起始地址、大小、状态 -
动态分区分配
(1)分配算法:
首次适应:空闲分区按照地址递增次序排列,每次分配内存,顺序查找第一个能满足大小的空闲分区,分配给作业
邻近适应(循环首次适应算法):相比于首次适应,改为分配内存时从上次查找结束的位置开始继续查找
最佳适应:空闲分区按照容量递增排列,每次分配内存的时候找到最小的且能满足大小的空闲分区;最容易产生内存碎片
最坏适应:空闲分区按照容量递减的次序排列,每次分配内存的时候找到最大的空闲分区分配 -
伙伴系统:伙伴系统规定:所有分区均为2的k次幂,k是正整数,所有相同大小的空闲分区,为他们单独设立一个空闲分区双向链表,需要给进程分配大小为n的空间时,先计算2(i-1)<n<2i,在分区大小为2i的空闲分区链表中找,找不到就在大小为2(i+1)的空闲分区链表中找,如果找到,把该分区分成两个相同大小分区,称为一对伙伴,其中一个分区用于分配,再把另一个分区加入大小为2^i的空闲分区链表中
(2)紧凑技术:
为了整合外部碎片,动态分区分配必须配合紧凑技术
(3)分区的分配和回收
分配:根据系统的分配算法,找到空闲分区表中对应的分区,如果进程需要空间<空闲分区空间,则把空闲分区分割后,多余的空闲空间留在空闲分区表中,根据需求排序,其余的分配给发出申请的进程
回收:进程运行完毕后释放内存时,系统根据回收区的首址,从空闲表中找到对应插入点,根据情况选择是否与其他空闲分区合并
4.2.2 离散分配
-
分页:
将主存和进程划分为大小相等的页框和页,将一个进程的所有页离散地调入页框
地址结构:页号+页内偏移量
一个进程对应一张页表,页表起始地址放在PCB中
TLB快表 -
分段
从程序员的角度提出,为了方便编程,可将进程按照逻辑分段,段的大小不一致,不对主存做任何划分
地址结构:段号+段内地址
段表:基址+长度 -
段页式
先把用户程序分成段,再把每个段分成页,并为每个段赋予一个段名,主存则划分页框
地址结构:段号S+段内页号P+页内偏移量W
每个进程建立一张段表,每个段对应一个段表,段表项至少包括段号、页表长度、页表始址,每个段有一张页表,每个页表项至少包括页号和页框号
4.3 多级页表
-
构造:将页表(二级页表)分页,离散的存放在内存里,再来一张表(一级页表、顶级页表、页目录)记录它们的位置
-
特点:多级页表的每个页表最多只能占一个页框
-
地址结构:页目录号+页号+页内偏移
根据页目录号字段确定顶级页表中的表项,这个表项存放了二级页表所在的页框号
根据页号字段确定二级页表中的表项,这个表项存放了要找的页所在的页框号
将页框号拼上页内偏移字段即可得到物理地址
4.4 碎片问题
-
内部碎片
给进程分的空间>进程需要的空间
常见于:基本分页、单一连续分配、固定分区分配
不会产生内部碎片:基本分段、动态分区分配(有可能产生,但可能性较小) -
外部碎片
内存中因为太小没办法分配出去的空间
常见于:动态分区分配
不会产生外部碎片:基本分页、固定分区分配
4.5 虚拟存储
4.5.1 内存分配
-
固定分配:为每个进程分配固定数目的物理块,进程运行期间不再改变
-
可变分配:先为每个进程分配一些物理块,运行期间可以根据情况增加减少
-
局部置换:如果进程在运行中发现了缺页,只能从分配给该进程的n个页框中选择一页换出再调入一页,这样来保证分配给该进程的内存空间不变
-
全局置换:如果进程运行过程中发现缺页,OS会在空闲页框中取出一块分配给该进程,这种策略下凡是产生缺页的进程都会获得新的页框,空闲页框队列中的页框用完时OS才能从内存中选择一页调出,被选择调出的页可能是任何一个进程的,被选中的进程拥有的页框会减少
策略:固定分配局部置换、可变分配全局置换、可变分配局部置换
4.5.2 调页
-
对换区:对换区通常用连续分配方式组织磁盘块,因此读写速度更快,若对换区空间足够,所需页面将从对换区调入,在进程运行前需将与该进程有关的文件从文件区拷贝到对换区
-
文件区:文件区通常采用离散分配方式组织磁盘块,因此读写速度更慢,若系统缺少足够的对换区空间,凡是不会被修改的文件都直接从文件区 调入,这样换出的时候不用写回磁盘,而对于可能被修改的部分换出的时候调到对换区,以后需要再从对换区调入
UNIX方式:凡是未运行过的页面都从文件区调入;运行过被换出的页面放在对换区,下次从对换区调入;允许页面共享,进程请求的页面可能已被其他进程调入内存,此时就无需从对换区调入
4.5.3 页面置换算法
-
最佳OPT置换算法:每次选择未来最长时间不会被访问的页面进行淘汰
-
先进先出FIFO置换算法:每次淘汰在内存里驻留最久的页面,FIFO算法可能发生Belady异常(为进程分配的物理块增多,缺页次数不减反增的异常现象)
-
最近最久未使用LRU置换算法:每次选择最近最久未被使用的页面淘汰
-
时钟CLOCK置换算法:给所有页面设置访问位,将内存中的所有页链接成一个循环队列,绕圈检查;访问位=0则换出,访问位=1则置0后扫描下一页
-
改进型CLOCK置换算法:(访问位,修改位);淘汰优先级:(0,0) > (0,1) > (1,0) > (1,1)
4.5.4 抖动
页面置换过程中,刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出内存,这种频繁的页面调度称为抖动
根本原因时分配给每个进程的物理块太少,已经不能满足进程正常运行的基本要求,每个进程运行时频繁的页面调度导致CPU利用率急剧下降
工作集:某段时间间隔内,进程要访问的页面集合(需求侧)
驻留集:给进程分配物理块的多少(供给侧)
驻留集的大小不能小于工作集
5. 文件管理
5.1 文件的逻辑结构
-
顺序文件:顺序排列,其中的记录是定长记录或变长记录
-
索引文件:为可变长记录文件建立一张索引表,为每个记录设置一个表项
-
顺序索引文件:为每个文件建立一张索引表时,为一组记录中的第一个记录建立一个索引表项
5.2 文件的物理结构
-
连续分配:为每一个文件分配一组相邻接的盘块,这样形成的文件结构称为顺序文件结构,此时的物理文件称为顺序文件。
优点:支持随机访问磁盘块;磁盘读写速度最快,因为文件占有的盘块会处于一条或几条相邻的磁道上,磁头寻道时间最少。
缺点:必须提供连续可用的磁盘块,容易产生外部碎片;必须事先知道文件的长度;插入和删除记录很麻烦;不适合会动态增长的文件。 -
链接分配:
(1)隐式链接:文件目录的每个目录项中,都必须含有指向链接文件第一个盘块和最后一个盘块的指针,在每个盘块中都含有指向下一个盘块的指针。
优点:消除了磁盘的外部碎片,提高了外存利用率;插入、删除和修改记录的操作都非常容易;能适应文件的动态增长,无需事先知道文件的大小。
缺点:不支持随机访问,只能顺序访问(遍历);可靠性差,如果一个链接指针出问题,整个链都会断。
(2)显示链接:
把用于链接文件各物理块的指针显式地存放在内存的一张链接表中,这张表叫文件分配表FAT,整个磁盘中仅设置一张,表的序号是物理盘块号,每个表项中存放链接指针。
优点:支持顺序访问(遍历)和随机访问(通过遍历FAT表查找第i块,不需要访问前i-1块);FAT在系统启动时就被读入内存,检索也是在内存中进行的,减少了访问磁盘的次数。
缺点:需要占用一些内存空间。 -
索引分配:
(1)单级索引分配:把每个文件的所有盘块号集中的放在一起,为每个文件分配一个索引块(表),将分配给该文件的所有盘块号都记录在该索引块中。
(2)多级索引分配:文件太大,索引块太多的时候,应该为这些索引块再建立一级索引,称为主索引,这样就形成了二级索引分配方式。
(3)索引分配优点:支持随机访问(索引块的第i个条目即为文件第i个块);不会产生外部碎片。
(4)索引分配缺点:每个文件必须有一个索引块,文件很小时,索引块里没多少信息,索引块空间利用率低,文件很大的时候又要占用几个索引块,此时可用链指针将索引块连起来,但很低效。
5.3 外存管理
-
空闲表法:属于连续分配方式,和内存的动态分配方式差不多,为每个文件分配一块连续的存储空间,再为所有外存的空间区域建立一张空闲表
-
空闲链表法:将所有空闲盘区拉成一条空闲链,根据构成链条元素,分成空闲盘块链和空闲盘区链
-
位示图法:利用二进制的0表示盘块空闲,1表示盘块已分配,这样所有盘块对应的比特构成一个集合,叫做位示图
-
成组链接法:空闲盘块号栈中存放了本栈中空闲盘块的数量,栈底记录的是下一个这样的一组数据所在的盘块号,栈底如果是0,就是空闲盘块链的结束标志,栈中除了栈底记录的是空闲盘块号栈,其他记录的都是空闲的盘块;栈是临界资源,需要互斥访问;分配盘块时,从栈顶取出空闲盘块号分给用户,然后栈顶指针向下移动一位,记录空闲盘块的数量减一,若已经到了栈底,就把记录的下一个空闲盘块号分配出去;回收同理,若栈满则存入盘块,再找新盘块作为栈底
-
FAT也可用于空闲空间管理
5.4 目录
-
FCB
(1)基本信息:文件名、文件物理位置、文件逻辑结构、文件物理结构等
(2)存取控制信息:文件主和各个用户的存取权限
(3)使用信息:文件的建立时间、上一次修改的时间、当前使用信息 -
索引节点inode
(1)磁盘inode:文件物理地址、文件链接计数(指向该文件的文件名的数量)、文件主标识、文件类型、文件存取权限、文件长度、文件存取时间
(2)内存inode又增加了:内存索引节点编号、访问计数、状态、逻辑设备号、链接指针
(3)inode结构
模式、所有者、时间戳、块大小、计数、直接块、一级间址、二级间址、三级间址
inode决定文件个数上限 -
目录和FCB的关系
单级目录结构:整个系统中只有一张目录表,因此文件不能重名
多级/树形目录结构:类比Windows文件管理
思路:目录=文件夹、FCB=目录项
5.5 文件共享
硬链接:硬链接++则count++,硬链接--则count--
软链接:软链接增加,软链接创建的link文件对应的新inode的count恒为1,因为软链接只是一个独立的文件,默认情况下不会有其他硬链接指向它的inode,也很少考虑删除软链接
5.6 补充
5.6.1 常见系统调用
-
open(pathname,flags,model)
pathname:要打开或创建的文件的路径
flags:控制文件打开模式的标志,如R(只读)、W(只写)
model:如果创建新文件,需指定文件的访问权限 -
read(fd,buf,count) / write(fd,buf,count)
fd:文件描述符,指向由open()打开的文件
buf:数据读取后存储的缓冲区指针
count:要读取的字节数 -
close(fd)
fd:文件描述符,指向由open()打开的文件
5.6.2 打开文件表
-
系统打开文件表
整个系统只有一张,记录所有打开文件的情况
表项中包含指向文件的内存inode的指针等
在OS导论中,文件第一次被打开时,会在系统打开文件表建立表项,后续如果再次被别的进程打开不再重复建立
在linux/unix中,每次使用open()都会在系统打开文件表建立一个表项 -
用户/进程打开文件表
每个进程一张,记录该进程打开文件的情况
表项中包含文件描述符、指向系统文件打开表项的指针、进程对文件的读写指针
6. IO系统
6.1 IO层次结构
6.1.1 用户层软件
-
假脱机技术(SPOOLing):
输入缓冲区和输出缓冲区:是在内存开辟的两个缓冲区,用来暂存输入设备传入的数据和从输出井准备传给输出设备的数据
输入进程和输出进程:输入进程也称为预输入进程,模拟脱机输入时的外围控制机,将用户要求的数据从输入设备送到输入缓冲区,再存放到输入井;CPU需要输入设备时,直接从输入井读入内存;输出进程也称缓输入进程,用于模拟脱机输出时的外围控制机,把数据从内存传送并存放在输出井,输出设备空闲时再从输出井经过缓冲区输出到输出设备
输入井和输出井:是在磁盘上开辟出来的两个存储区域,输入井模拟脱机输入时的磁盘,用来收容IO设备输入的数据,输出井模拟脱机输出时的磁盘,收容用户程序的输出数据;输入输出井中的数据一般以文件的形式组织管理,把这些文件称为井文件;一个文件进存放某一个进程的输入或输出数据,所有进程的数据输入文件连接成一个输入队列,输出同理
井管理程序:控制作业和磁盘井之间的信息交换,作业执行过程中发出输入输出请求时,操作系统调用井管理程序,控制信息输入输出
特点:提高了IO速度;把独占设备改造成共享设备;实现了虚拟设备功能 -
应用程序接口:
块设备接口
流设备接口
网络设备接口
6.1.2 设备独立性(无关性)软件
-
IO调度
-
设备的分配与回收
系统设备表SDT:设备类、设备标识符、DCT、驱动程序入口
设备控制表DCT:设备类型、设备标识符、设备状态、指向控制器表的指针、重复执行次数或时间、设备队列的队首指针
控制器控制表COCT:控制器标识符、控制器状态、与控制器连接的通道表指针、控制器队列的队首指针、控制器队列的队尾指针
逻辑设备表LUT:整个系统设置一张LUT,记录所有进程的分配情况,这样就不能有相同的逻辑设备名,多用户环境下难以做到;每个用户设置一张LUT,用户登录时就为用户建立一个进程,同时建立LUT,并放入PCB中。 -
缓冲区管理
磁盘把数据输入到缓冲区的时间为T,OS把缓冲区数据传到用户区时间为M,CPU处理时间为C
单缓冲区:T=C时,处理一块数据的时间是M+T或M+C;T>C时,处理一块数据的时间是M+T;T<C时,处理一块数据的时间是M+C。每一块数据处理时间为Max(C,T)+M
双缓冲区:若C < T,可让块设备连续输入;若C > T,可使CPU不必等待设备输入。每一块数据处理时间为Max(C+M,T)
6.1.3 设备驱动程序
-
功能
接受上层软件发来的命令和参数,转换为与设备相关的底层操作序列(如逻辑盘块号转换为磁盘的磁道号、扇区号)
检查用户IO请求的合法性,了解IO设备的工作状态,传递与IO设备操作有关的参数,设置设备的工作方式
发出IO命令,如果设备空闲则启动IO设备,设备忙碌则将请求者的请求挂在队列等待
及时响应由设备控制器发来的中断请求,根据其中断类型,调用相应的中断处理程序进行处理
-
特点
驱动程序是实现在于设备无关的软件和设备控制器之间通信和转换的程序,它将抽象的IO请求转换成具体的IO操作后传送给控制器,又把控制器中记录的设备状态和IO操作完成情况反映给请求IO的进程
驱动程序与设备控制器和IO设备的硬件特性紧密相关,不同的设备要配置不同的驱动程序。但可以为相同的多个终端设置一个终端驱动程序
驱动程序与IO设备所采用的IO控制方式紧密相关,常用IO控制方式是中断驱动和DMA方式
驱动程序与硬件紧密相关,一部分必须用汇编语言书写
驱动程序应允许可重入,正在运行的驱动程序常会再一次调用完成前被再一次调用
6.1.4 中断处理程序
用户保存被中断进程的CPU环境,转入相应的中断处理程序进行处理,处理完毕后恢复被中断进程的现场后,返回被中断进程
6.1.5 硬件
6.2 磁盘
6.2.1 磁盘格式化
低级格式化(物理格式化):将磁盘分成扇区,以便磁盘控制器能够进行读/写操作;一些非常底层、非常硬件、非常不可能由用户拿到磁盘才会做的事情
磁盘分区:将磁盘分成C盘、D盘等;创建分区表
高级格式化(逻辑格式化):将初始文件系统数据结构存储到磁盘上,包括空闲分区、已分配空间
6.2.1 调度算法
先来先服务FCFS
最短寻道时间优先SSTF:可能产生饥饿现象
扫描SCAN:在SSTF的基础上规定磁头移动方向,只有碰到最内外侧端点才允许反向移动,来防止产生饥饿现象
循环扫描C-SCAN:在SCAN算法的基础上,规定磁头只能单向移动,返回的时候直接返回起始端不服务任何请求
浙公网安备 33010602011771号