书《计算机操作系统》第四版(汤小丹编著) 课程操作系统 操作系统启动流程略了 md和pdf下载:Chasssser 完整版包括收集的题目 以下仅为知识点
操作系统是配置在计算机硬件上的第一层软件,是对硬件系统的首次扩充。其主要作用是管理好这些设备,提高他们的利用率和系统的吞吐量,并为用户和应用程序提供一个简单的接口,便于用户使用。(第一章第一段)
计算机系统中同时存在多个运行的程序,需要OS管理和调度。
并行性:两个或者多个事件在同一时刻发生。
并发性:两个或者多个事件在同一时间间隔内发生。
“同时”访问:有的资源允许一段时间内由多个进程"同时"对它们进行访问,"同时"在微观上意义上,进程对该资源的访问是交替进行的。
互斥共享:有的资源虽然可以供给多个进程使用,但是一段时间内只允许一个进程访问该资源,故建立进程对这类资源的互斥访问。
利用多道程序设计技术,让每个用户都觉得有一个计算机专门为他服务
时分复用:利用某设备为一用户服务的空闲时间内,又转去为其他用户服务。通过利用处理机的空闲时间运行其它程序,提高处理的利用率
空分复用:利用存储器的空闲空间分区域存放和运行其他的多道程序,以此提高内存的利用率。
程序的执行不是一贯到底,而是走走停停,向前推进的速度不可预知,这就是进程的异步性。
但是只要运行环境相同,OS需要保证程序运行的结果也要相同
进程控制:为作业创建进程、撤销(终止)已结束的进程、控制进程在运行过程中的状态转换
进程同步:为多个进程(线程)的运行进行协调。
进程通信:实现相互合作进程之间的信息交换。
内存分配
内存保护
地址映射
内存扩充
缓冲管理:缓和CPU和I/O设备速度不匹配的矛盾。
设备分配:设备控制表、控制器控制表
设备处理:设备驱动程序,实现CPU和设设备控制器之间的通信。
文件存储空间的管理
目录管理
文件的读/写管理和保护
用户接口
程序接口
系统安全
网络的功能和服务
支持多媒体
■ 操作系统=装载器+通用子程序库
■ 问题:昂贵组件的低利用率
■ 顺序执行与批处理
■ 主要缺点:系统中的资源得不到充分利用,因为内存中只有一道程序
■ 保持多个工作在内存中并且在各工作间复用CPU
■ 多道程序交替执行,交替的条件是前一个正在执行的程序主动让出CPU的使用权。
■ 多道批处理系统主要考虑的是系统效率和系统的吞吐量(优点)。
■ 多道批处理系统缺点:平均周转时间长,无交互能力
■ 定时中断用于工作对CPU的复用
■ 交互性和及时性是分时系统的主要特征。
■ 实时系统的正确性,不仅有计算的逻辑结果来决定,还取决于产生结果的时间。
■ 实时任务的类型:
周期性实时任务和非周期性实时任务,前者是指外部设备周期性地发出激励信号给计算机,使其周期性执行,以便周期性地控制某外部设备,后者无明显的周期性,但是都必须联系着一个截止时间。
硬实时和软实时,前者必须满足对截止时间的要求,后者对此没有严格要求
无结构
模块化
分层
自底向上的分层原则,确定每一步的设计都是建立在可靠的基础上。
微内核结构
只将最基本的部分放入微内核中。
防止OS本身及相关数据遭到应用程序或无意的破坏,通常将处理机的执行状态分为:
系统态(内核态,内核态又称为管态):高权限,能访问所有的寄存器。
用户态:低权限,能访问指定的寄存器。
例子:
CPU执行操作系统代码的时候称为处理机处于管态。
函数调用并不会切换到内核态,而除零操作引发中断,中断和系统调用都会切换到内核态进行相应处理。
计算机的一些功能只有内核有权利访问,通过中断、异常和系统调用为应用程序提供方便。
在计算机运行中,内核是被信任的第三方
只有内核可以执行特权指令
方便应用程序
当外设连接计算机时,会出现什么现象?(中断)
当应用程序处理意想不到的行为时,会出现什么现象?(异常)
通过调用函数库,函数库又会调用对应的系统调用接口,从而得到系统服务。
系统调用时会有堆栈切换和特权级的转换,INT和IRET用于系统调用。
功能调用时没有堆栈切换,CALL和RET用于功能调用。
我们要解决用户程序如何来解决系统的服务。就好象说我们提供给银行对外提供服务,银行为了保证安全,它有很多的防护,这个防护又和对外提供服务这是有矛盾的。
为了方便用户来使用银行的服务,应该必须提供灵活的访问接口,又不能影响到银行的安全。
操作系统内核也是一样的,我们需要来通过系统调用来提供一个接口,让应用程序既方便的使用内核提供的服务,又不至于用户的行为对我内核的安全产生影响
我正在处理一个请求的时候,又来了一个请求这时候我怎么办,那我们说在操作系统的里头呢?
它是硬件的中断,它是允许被打断的,也就是说我正在处理一个中断的时候,可以允许你再出现其他的中断。
如果两个中断源不同,那这时候我可以通过优先级的高低,让一个往后推一段,或者说让一个暂停下来,那使得我可以同时在做交替在做处理。
中断服务例程里头,并不是说我任何一个时刻都可以做任何一个处理,它会在一定的时间里禁止中断请求。
比如说我电源有问题,那可能其他的问题就变得不重要了,这时候我在做电源的处理的时候,我就会禁止掉其他中断。中断服务请求会一直保持到CPU做出响应。
比如说我在这里头虚拟存储里头,它访问到的存储单元的数据不存在,我正在从硬盘上倒数据进来。倒的过程当中,它会用到磁盘I/O,这时候也会再有磁盘设备的中断,这时候是允许它可以做嵌套的。
系统调用是提供给应用程序使用的,由用户态发出,进入内核态执行。外部中断随时可能发生;应用程序执行时可能发生缺页;进程切换完全由内核来控制。
进程是指一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。
进程包含了正在运行的一个程序的所有状态信息 :
内核选择一个就绪的进程,让它占用处理机并运行
如何选择?处理机调度算法
只有进程本身才知道何时需要等待某种事件的发生,即导致其进入等待状态的一定是进程本身内部原因所导致的,不是外部原因所导致的。
进程只能被别的进程或者操作系统给唤醒。
一个新进程被产生出来执行一个程序
当进程被创建完成并初始化后,一切就绪准备运行时,变为就绪状态
处于就绪状态的进程被进程调度程序选中后,就分配到处理机上运行
当进程表示它已经完成或者因出错,当前运行进程会由操作系统作结束处理(回收资源)
处于运行状态的进程在其运行期间,由于分配给它的处理时间片用完而让出处理机
当进程请求某资源且必须等待时
当进程要等待某事件到来时,它从阻塞状态变到就绪状态。
处在挂起状态的进程映像在磁盘上,目的是减少进程占用内存
进程在外存并等待某事件的出现(多加了一个关于进程的位置信息)
进程在外存,但只要进入内存,即可运行
(无法进入内存原因:内存空间不够或者进程本身优先级不够高)
没有进程处于就绪状态或者就绪进程要求更多内存资源
当有高优先级等待(系统认为会很快就绪的)进程和低优先级就绪进程
对抢先式分时系统,当有高优先级等待挂起进程因为事件出现而进入就绪挂起(比如内存不够)
当有等待挂起进程因为相关事件出现
没有就绪进程或者挂起就绪进程优先级高于就绪进程
当一个进程释放足够内存,并有高优先级等待挂起进程
就绪队列、各种等待队列
进程状态变化时,它所在的PCB会从一个队列
换到另一个
进程通信是进程之间的信息交换,是进程进行通信和同步的机制。
进程不借助任何共享存储区或数据结构,而是以格式化的消息(message)为单位,将数据封装在消息中,并利用操作系统提供一组通信命令(原语)完成信息传递和数据交换。
发送进程利用OS所提供的通信指令,直接把消息放到目标进程
(1)对称寻址方式;该方式要求发送和接受进程必须以显示方式提供对方的标识符。
系统提供命令:
send(receiver,message);//发送一个消息给接受进程receiver
receive(sender,message);//接受进程sender发来的消息
(2)非对称寻址方式;在接受程序原语中,不需要命名发送进程。
系统提供命令:
send(P,message);//发送一个消息给接受进程P
receive(id,message);//接受来自任何进程的消息,id变量可以设置为发送进程的id或者名字
(1)定长(消息长度)
(2)变长(消息长度)
(1)发送阻塞,接收阻塞
(2)发送不阻塞,接收阻塞
(3)发送不阻塞,接收不阻塞
建立方式:(1)显示建立链接命令;(2)发送命令自动建立链路
通信方式(1)单向(2)双向
(增加了消息队列的队首指针,互斥和资源信号量)
发送原语首先根据发送区a中的消息长度a.size来申请一个缓冲区i,接着把a中的信息复制到缓冲区i中。获得接受进程内部标识符j,然后将i挂在j.mq上,由于该队列属于临界资源,所以执行insert前后都要执行wait和signal操作。
其中
mq//消息队列
mutex//对消息队列的互斥访问
sm//消息的资源信号量
调用接受原语receive(b),从自己的消息缓冲队列mq中摘下第一个消息缓冲区i,并将其中的数据复制到以b为首地址的指定消息接收区内。
发送和 接收进程,通过共享中间实体(邮箱 )完成通信。该实体建立在随机存储器的公用缓冲区上,用来暂存信息。
信箱头:用于存放信箱的描述信息,如信箱标识符等
信箱体:由若干个可以存放信息的信箱格组成,信箱格数目和大小是在创建信箱时确定的。
(1)邮箱的创建和撤销
(2)消息的发送和接收
Send(mailbox,message);//将一个消息发送到指定邮箱
Receive(mailbox,message);//从指定邮箱中接受一个消息
(1)私用邮箱:只有创建者才能接收消息
(2)公用邮箱:操作系统创建
(3)共享邮箱:创建进程指明接收进程
(1)一对一:专用通信链路
(2)多对一:客户/服务器
(3)一对多:广播
(4)多对多:公共邮箱
其中
共享内存是把同一个物理内存区域同时映射到多个进程的内存地址空间的通信机制
每个进程都有私有内存地址空间
每个进程的内存地址空间需明确设置共享内存段
同一进程中的线程总是共享相同的内存地址空间
快速、方便地共享数据;
最快的通信方法;
一个进程写另外一个进程立即可见;
没有系统调用干预;
没有数据复制;
不提供同步,必须用额外的同步机制来协调数据访问,比如由程序员提供同步
一个嵌套字就是一个通信标识类型的数据结构,包含通信目的的地址,端口号,传输层协议等
基于文件型:两个进程都运行在同一台机器上,嵌套字基于本地文件系统支持
基于网络型:非对称通信方式,需要发送者提供接收者的命名
不仅使用与同一台计算机内部的进程通信,而且适用于网络环境中不同计算机之间的进程通信。
在OS中引入进程是为了让多个程序能并发执行,来提高资源利用率和系统吞吐量。
在OS中映入线程是为了减少程序在并发执行时所付出的时空开销,使得OS有更高的并发性。
线程是进程的一部分,描述指令流执行状态,它是进程中的指令执行流的最小单元,是CPU调度的基本单位。
调度的实质是一种资源分配,处理机调度是对处理机资源进行分配。
处理机调度决定系统运行时的性能:系统吞入量、资源利用率、作业周转时间、作业响应时间等….
高级调度(又称长程调度或者作业调度)-》作业级
低级调度(又称短程调度或者进程调度)-》进程(线程)级
中级调度(内存调度)-》内存
在进程/线程的生命周期中的什么时候进行调度?
比如3个进程,计算时间为12,3,3,到达顺序为P1,P2,P3(假设同一时刻到达)
Mon 06Mon 13P1 P2 P3 任务执行顺序
则周转时间=(12+15+18)/3=15
如果到达顺序为P2,P3,P1
Mon 06Mon 13P2 P3 P1 任务执行顺序
则测试周转时间=(3+6+18)/3=9
简单
平均等待时间波动比较大,比如短进程可能排在长进程后面
选择就绪队列中执行时间最短的进程占用CPU进入运行状态
就绪队列按照预期的执行时间长度来排序
新进程所需要的执行时间比当前正在执行的进程剩余的执行时间还要短,那么允许它抢先。
SPN算法中一组进程的平均周转时间
此时周转时间=(r1+r2+r3+r4+r5+r6)/6=(r_1+r_2+r_3+r_4+r_5+r_6)/6=(r1+r2+r3+r4+r5+r6)/6
修改进程执行顺序可能减少平均等待时间吗?
周转时间=(r1+r2+r4−c3+r5−c3+r4+c4+c5+r6)/6=(r_1+r_2+r_4-c_3+r_5-c_3+r_4+c_4+c_5+r_6)/6=(r1+r2+r4−c3+r5−c3+r4+c4+c5+r6)/6
=(r1+r2+r3+r4+r5+r6+(c4+c5−2c3))/6=(r_1+r_2+r_3+r_4+r_5+r_6+(c_4+c_5-2c_3))/6=(r1+r2+r3+r4+r5+r6+(c4+c5−2c3))/6
cic_ici表示进程PiP_iPi的执行时间(不一定等于周转时间)
选择就绪队列中响应比R值最高的进程
即按照R值来排序
R=(w+s)/s
w: 等待时间(waiting time)
s: 执行时间(service time)
时间片结束时,按照FCFS算法切换到下一个就绪进程
每隔(n-1)个时间片进程,进程执行一个时间片q
进程在一个时间片内已执行完,立刻调度,并将该进程从就绪队列中删除
进程在一个时间片内未执行完,立刻调度,并将该进程放入就绪队列末尾
如:前台(交互)、后台(批处理)
如:前台–RR、后台–FCFS
设置多个就绪队列,为每个队列赋予不同的优先级,每个队列采用FCFS算法,按队列优先级调度
对多个进程在执行顺序上进行调节,使并发执行的诸程序之间能按照一定的规则(时序)共享系统资源,并能够很好的相互合作,从而使程序的执行具有可再现性。
间接相互制约关系(互斥):由于共享系统资源导致
直接相互制约关系(同步):为完成同一任务而合作
比如打印机,磁带机,producer-consumer问题
enter section //进入区
critical section //临界区
exit section //退出区
remainder seciton//剩余区
空闲让进:无进程时,任何进程可进去
忙则等待:有进程在临界区时,其他进程均不能进入临界区
有限等待:有点等待时间,不能无限等待
让权等待(可选):不能进入临界区的进程,应释放CPU
软件实现方法,硬件实现方法。
local_irq_save(unsigned long flags); //关中断
critical section //临界区
local_irq_restore(unsigned long flags); //使能中断
满足线程TiT_iTi和TjT_jTj之间互斥的经典的基于软件的方法
int turn; //表示允许进入临界区的线程ID
boolean flag[]; //表示进程请求进入临界区
flag[i]=true;
turn=j;
while(flag[j] && turn == j); //有一个条件不满足就进入临界区,否则一直等
/*
*此时如果同时有两个进程进入临界区
*那么先写的那个进程能进入(后一个不满足),后的不能(都满足)
*/
flag[i]=false;
线程TiT_iTi的代码
do{
flag[i]=true; //线程i请求进入临界区
turn=j;
while(flag[j] && turn == j);
CRITICAL SECTION //临界区
flag[i]=false;
REMAINDER SECTION //退出区
}while(true);
flag[0]=false;
flag[1]=false;
turn=0;
do{
flag[i]=true; //线程i请求进入临界区
while(flag[j]==true){
if(turn!=i){
flag[i]=false;
while(turn!=i){}
flag=true;
}
}
CRITICAL SECTION //临界区
turn=j;
falsg[i]=false;
REMAINDER SECTION //退出区
}while(true);
基于硬件提供了一些同步原语,比如中断禁用,原子操作指令等
操作系统提供更高级的编程抽象来简化进程同步,例如:锁、信号量,用硬件原语来构建
class Lock{
int value=0;
}
//忙等待锁
Lock::Acquire(){
while(test-and-set(value))
;//spin
}
Lock::Release(){
value=0;
}
复杂,需要两个进程间的共享数据项
需要忙等待,浪费CPU时间
信号量是操作系统提供的一种协调共享资源访问的方法
由一个整形变量sem(共享资源的数目)和两个原子操作组成
//P操作--申请使用资源
P()(Prolaag (荷兰语尝试减少)) -》wait
sem--;//可用资源减少一
if sem<0,进入等待,否则继续 //可用资源用完了,需要等待其他线程释放资源
//V操作--释放可用资源
V()(Verhoog (荷兰语增加)) -》signal
sem++;
if sem<=0,唤醒一个等待进程
不能。因为自旋锁需要占用CPU,随时检查,有可能临界区的使用者退出时刚修改完,下一个进入者进入时资源才变成有效,就无法实现先进先出。
classSemaphore{
int sem; //共享资源数目
WaitQueue q; //等待队列
}
Semaphore::P(){
sem--;
if(sem<0){
//资源用完了
Add this thread t to q;
block(p); //阻塞
}
}
Semaphore::V() {
sem++;
if (sem<=0) {
//此时前面仍有等待线程
//从对应的等待队列里把相应的线程放入就绪队列
Remove a thread t from q;
wakeup(t);
}
}
每个临界区设置一个信号量,其初值为1
mutex = new Semaphore(1); //信号量初始化为1
//控制临界区的访问
mutex->P(); //信号量计数--
Critical Section;
mutex->V(); //释放资源,信号量计数++
注意:
初始化如果是同步互斥,看资源数目,如果是条件同步,为0或者1
必须成对使用P()操作和V()操作
P()操作保证互斥访问临界资源
PV操作不能次序错误、重复或遗漏(但不要求P在V之前或者之后)
执行时不可中断
问题:
不申请直接释放,出现多个线程进入临界区的情况
只申请不释放,缓冲区没有线程,但是谁也进不去临界区
//此时的条件同步设置一个信号量,初始化为0
condition =new Semaphore(0);
//实现一个条件等待,线程A要等到线程B执行完X模块后才能执行N模块
//线程A
---M---
condition->P();
---N---
//线程B
---x---
condition->V();
---Y---
//在B里释放信号量,使其0->1,如果B先执行完X模块,则A可以直接往下执行;
//如果A先执行完就等待
管程是一种用于多线程互斥访问共享资源的程序结构
管程的使用
控制管程代码的互斥访问
管理共享数据的并发访问
如果是0个,就等同与一个临界区,如果是多个就是管程所特有的
Class Condition{
int numWaiting=0;
//条件变量初值为0,如果在信号量里和资源数目一致
WaitQueue q;
}
Condition::Wait(lock){
numWaiting++; //等待数目++
Add this thread t to q;//将自己放入等待队列当中
release(lock); //释放管程的互斥访问权限
shedule();//执行调度,切换线程need mutex
require(lock);//请求访问权限
}
Condition::Signal(){
if(numWaiting>0){//等待队列不为空,即有另外的线程等待这个条件变量上,每个变量对应一个队列
Remove a thread t from q;//将此线程从等待队列移动到就绪队列中
wakeup(t);//唤醒进程need mutex
numWaiting--;//等待数目--
}
}
classBoundedBuffer {
…
Lock lock;//一个入口等待队列
int count = 0; //写入缓冲区的数据的数目
Condition notFull, notEmpty;//两个条件变量
}
BoundedBuffer::Deposit(c) {//生产者
lock->Acquire(); //管程进入权申请
while (count == n) //n个缓冲区中都有数据了--对应**1
notFull.Wait(&lock);//就放弃管程使用权,等在notfull条件变量上
Add c to the buffer;
count++;
notEmpty.Signal();
lock->Release(); //管程进入权释放
}
BoundedBuffer::Remove(c) {//消费者
lock->Acquire(); //管程进入权申请
while (count == 0) //如果没数据
notEmpty.Wait(&lock);//就放弃管程使用权,等在非空条件上
Remove c from buffer;
count--;
notFull.Signal(); //读出一个数据后就释放notfull--对应**1
lock->Release(); //管程进入权释放
}
1.初始化
2.管程中写法
5个哲学家围绕一张圆桌而坐,桌子上放着5支叉子,每两个哲学家之间放一支
哲学家的动作包括思考和进餐,进餐时需同时拿到左右两边的叉子,思考时将两支叉子放回原处
如何保证哲学家们的动作有序进行?如:不出现有人永远拿不到叉子
#define N 5 // 哲学家个数
semaphore fork[5]; // 信号量初值为1
void philosopher(int i) // 哲学家编号:0 - 4
while(TRUE)
{
think( ); // 哲学家在思考
if (i%2 == 0) {
P(fork[i]); // 去拿左边的叉子
P(fork[(i + 1) % N]); // 去拿右边的叉子
} else {
P(fork[(i + 1) % N]); // 去拿右边的叉子
P(fork[i]); // 去拿左边的叉子
}
eat( ); // 吃面条中….
V(fork[i]); // 放下左边的叉子
V(fork[(i + 1) % N]); // 放下右边的叉子
}
//没有死锁,可有多人同时就餐
用信号量描述每个约束
semphoare WriteMutex=1;
int Rcount=0;
semphoare CountMutex=1;
void Writer(){
P(WriteMutex);
write;
V(WriteMutex);
}
void Reader(){
P(CountMutex);
if (Rcount == 0)
P(WriteMutex);
++Rcount;
V(CountMutex);
read;
P(CountMutex);
--Rcount;
if (Rcount == 0)
V(WriteMutex);
V(CountMutex)
}
//此实现中,读者优先
读者优先策略
只要有读者正在读状态,后来的读者都能直接进入
如读者持续不断进入,则写者就处于饥饿
写者优先策略
只要有写者就绪,写者应尽快执行写操作
如写者持续不断就绪,则读者就处于饥饿
如何实现?
用信号量描述每个约束
semaphore WriteMutex=1,ReadMutex=1,x=1;
int Rcount=0,WRcount=0;
semaphore CountMutex=1,WRMutex=1;
void Reader(){
P(x);
P(ReadMutex);
P(CountMutex);
if(Rcount==0)
P(WriteMutex);
++Rcount;
V(CountMutex);
V(ReadMutex);
V(x);
read;
P(CountMutex);
Rcount--;
if(Rcount==0)
V(WriteMutex);
V(CountMutex);
}
void Writer(){
P(WRMutex);
if(WRcount==0)
P(ReadMutex);
WRcount++;
V(WRMutex);
P(WriteMutex);
Write;
V(WriteMutex);
P(WRMutex);
WRcount--;
if(WRcount==0)
V(ReadMutex);
V(WRMutex);
}
顶点
有向边
如果一组进程中每一个进程都在等待仅由该组进程中的其它进程才能引发的事件,那该组进程是死锁的。
确保系统永远不会进去死锁状态
预防是采用某种策略,限制并发进程对资源的请求,使得系统在任何时刻都不满足死锁的必要条件(四个)
在使用前进行判断,只允许不会出现死锁的进程请求资源
在检测到运行系统进入死锁状态后,进行恢复
通常操作系统忽略死锁,大多数操作系统(包括UNIX)的做法
在死锁避免方法中,把系统的状态分为安全状态和不安全状态。
当系统处于安全状态时,可以避免发生死锁。反之,可能发生死锁。
系统处于安全状态,一定没有死锁
系统处于不安全状态,可能出现死锁,避免死锁就是确保系统不会进入不安全状态
nnn = 线程数量, mmm = 资源类型数量
MaxMaxMax(总需求量)? n×m$矩阵
线程TiT_iTi最多请求类型RjR_jRj的资源$ Max[i,j] $个实例
AvailableAvailableAvailable(剩余空闲量):长度为mmm的向量
当前有 $Available[j] 个类型为个类型为个类型为R_j$的资源实例可用
AllocationAllocationAllocation(已分配量):n×mn×mn×m矩阵
线程$T_i ∗∗当前分配∗∗了**当前分配**了∗∗当前分配∗∗了 Allocation[i, j] 个个个R_j$的实例
NeedNeedNeed(未来需要量):n×mn×mn×m矩阵
线程$T_i $未来需要 Need[i,j]Need[i, j]Need[i,j]个RjR_jRj资源实例
满足公式:
Need[i,j]=Max[i,j]–Allocation[i,j]Need[i,j]= Max[i,j]–Allocation[i,j]Need[i,j]=Max[i,j]–Allocation[i,j]
RequestiRequest_iRequesti 线程TiT_iTi的资源请求向量
Requesti[j]Request_i[j]Requesti[j] 线程TiT_iTi请求资源RjR_jRj的实例
1.如果 Requesti≤Need[i]Request_i ≤ Need[i]Requesti≤Need[i], 转到步骤2。否则, 拒绝资源申请, 因为线程已经超过了其最大要求
2.如果Requesti≤AvailableRequest_i≤ AvailableRequesti≤Available, 转到步骤3。否则,TiT_iTi必须等待,因为资源不可用
3.通过安全状态判断来确定是否分配资源给TiT_iTi :
生成一个需要判断状态是否安全的资源分配环境
Available=Available−RequestiAvailable= Available -Request_iAvailable=Available−Requesti;//剩余-请求
Allocation[i]=Allocation[i]+RequestiAllocation[i]=Allocation[i]+Request_iAllocation[i]=Allocation[i]+Requesti;//已分配+请求
Need[i]=Need[i]–RequestiNeed[i]=Need[i]–Request_iNeed[i]=Need[i]–Requesti;//未来需要-请求
如果返回结果是安全,将资源分配给TiT_iTi
如果返回结果是不安全,系统会拒绝TiT_iTi的资源请求
1.Work 和Finish 分别是长度为m和n的向量初始化:
Work = Available //当前资源剩余空闲量
Finish[i] = false for i:1,2, …, n. //线程i没结束
2.寻找线程Ti:
(a) Finish[i] = false//接下来找出Need比Work小的线程i
(b) Need[i]≤Work
若找到,执行3;
没有找到满足条件的Ti,转4。
3.Work = Work + Allocation[i]//线程i的资源需求量小于当前剩余空闲资源量, 所以配置给它的资源再回收
Finish[i] = true 转2
4.如所有线程Ti满足Finish[i] == true,//所有线程的Finish为True,表明系统处于安全状态
则系统处于安全状态
允许系统进入死锁状态
维护系统的资源分配图
定期调用死锁检测算法来搜索图中是否存在死锁
出现死锁时,用死锁恢复机制进行恢复
将内存中暂时不能运行的程序调到磁盘的对换区,同时将磁盘上能运行的程序调入内存
整体对换(进程对换),以进程为单位,需要系统支持功能对对换空间的管理,进程的换入和进程的换出
页面(分段)对换,以进程的一个页面或者分段为单位,有称部分对换,其目的是支持虚拟存储系统
文件区用于存放各类文件,对换区用于存放从进程换出的进程
实现从逻辑地址到物理地址的变换,借助页表来完成
从进程发出指定逻辑地址的访问请求,经过地址变换,到内存中找到对应的实际物理单元取出数据的总时间。
主要满足用户和程序员以下需求:
用户把自己的作业按照逻辑管理划分为若干段,每个段都是从0开始编址,并有自己的名字和长度。因此,希望要访问的逻辑地址是由段名(段号)和段内偏移量(段内地址)决定的。
在实现对程序和数据的共享时,是以信息的逻辑单位为基础的。分页系统中的页只是存放信息的物理单位(块),并无完整的意义,段却是信息的逻辑单位。为了实现段的共享,希望存储管理能与用户程序分段的组织方式相适应。
有些段,会随着程序的使用不断增长。而事先又无法确切地知道数据段会增长到多大。
动态链接是指在作业运行前,并不把几个目标程序段链接起来。要运行时,先将主程序所对应的目标程序装入内存并启动运行,当运行过程中有需要调用某段时,才将该段调入内存并进行链接。可见动态链接也要求以段作为管理的单位。
段号+段内地址
段号可算一个作业最长有多少个段,段内地址可算每个段的最大长度
在分段存储管理方式中,作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息
在系统中为每个进程建立一段映射表,简称“段表”。每个段在表中占有一个表项。其中记录了该段在内存中的起始地址(基址)和段的长度。段表可以存放在一组寄存器中,以提高访问速度,但更常见的是将段表放在内存中。
在配置了段表后,执行中的进程可通过查找段表找到每个段所对应的内存区。
段表是用于实现从逻辑段到物理内存区的映射。
为了实现进程逻辑地址到物理地址的变换功能,在系统中设置了段表寄存器,用于存放段表起始地址和段表长度TL。在进行地址变换时,系统将逻辑地址中的段号S与段表长度TL进行比较。若S>TL,表示段号太大。访问越界,于是产生越界中断信号;若未越界,则根据段表的起始地址和该段的段号+段内地址从而到的要访问的内存物理地址。
段表放在内存中时,每次访问一个数据都要两次方寸,解决方法和分页系统类似,设置一个联想寄存器,来保存最近常用的段表项。
a)、页是信息的物理单位,分页是为实现离散分配方式,其目的是消减内存的外零头,提高内存的利用率;分段中的段则是信息的逻辑单位,它含有一组其意义相对完整的信息,分段的目的是为了能更好地满足用户的需要。
b)、页的大小固定且由系统决定,由系统把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而在系统中只能有一种大小的页面;而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时,根据信息的性质来划分。
c)、分页的作业地址空间是一维的,分页完全是系统行为,即单一的线性地址空间,程序员只需利用一个记忆符,即可表示一个地址;而分段的作业地址空间则是二维的,分段是用户行为,程序员在标识一个地址时,既要给出段名,又要给出段内地址。
先将用户程序分段,在段内进行分页,为每一个段赋予一个段名。在段页式系统中,其地址结构由段号、段内页号及页内地址三部分所组成。
配置一个段表寄存器,其中存放段表起始地址和段表长TL。比较段号与TL是否越界,从段表寄存器中获取段表始址找到段表,根据段表内的页表始址找到对应的页表,在根据页表的存储块找到内存中的物理块,从而获取物理地址。
段页式系统中,为了获得一条指令或数据,须三次访问内存:
① 访问内存中的段表,从中取得页表始址
② 访问内存中的页表,从中取出该页所在的物理块号,并与页内地址形成物理地址
③ 访问真正从第二次访问所得的地址中,取出指令或者数据
多次访问内存,执行速度降低,因此在地址变换机构中增设一个高速缓冲寄存器。每次访问它时,都须同时利用段号和页号去检索高速缓存,若找到匹配的表项,便可以从中得到相应页的物理块号,用来与页内地址一起形成物理地址;若未找到匹配表项,则仍需要再三次访问内存。
在可变分区存储管理下,按地址排列的内存空闲区为:100KB、500KB、200KB、300KB和600KB。现有若干用户程序,其所需内存依次分别为212KB、417KB、112KB和426KB,分别用首次适应算法、最佳适应算法、最坏适应算法,将它们装入到内存的哪些空闲分区?哪个算法能最有效利用内存?
可变分区存储管理中,作业的撤离必定会修改内存的“空闲区表”,试画出因作业撤离修改“空闲区表”的四种情况。
根据回收区的首地址,在空闲分区表(链)找到插入点,此时可能出现4种情况之一(假设空闲分区表按地址从低到高顺序排列):
解:
在采用页式存储管理的系统中,某作业的逻辑地址空间为4页(每页4096字节),且已知该作业的页表如下表。试求出逻辑地址14688所对应的物理地址。
程序在执行时出现的局部性规律,即在一较短时间内,程序的执行仅仅局限于某个部分,相应地,它所访问的存储空间也局限与某个区域。
空间局限性:程序在一段时间内所访问地址可能集中在一定的范围内,其典型情况是程序的顺序执行
时间局限性:程序中的某条指令(数据)被执行(访问),不久后该指令可能再次被执行(访问)。产生的典型原因是在程序中存在大量的循环操作。
虚拟存储器,是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统。
程序被允许分成多次装入内存
允许将暂不使用的代码和数据从内存中调至外存的对换区
能从逻辑上扩充内存容量,使得用户看到的内存容量远大于实际内存容量
在分页系统的基础上增加了请求调页功能和页面置换功能所形成的页式虚拟存储系统,允许用户程序只装入少数页面的程序和数据即可启动运行,通过上述功能将即将运行的页面调入内存,同时将暂不运行的页面换出到外存。
在纯分页的页表机制上增加其他字段形成的数据结构,用于将逻辑地址转换为物理地址
每当用户程序需要的页面不再内存中,就产生缺页中断
选择的被淘汰页面是以后永远不使用的,或者是在最长(未来)时间内不再被访问的
总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰
选择最近最久未使用的页面进行淘汰
LRU需要为每个内存页面配置一个移位寄存器,来记录进程在内存中的各个页面使用情况
LRU还需要用一个特殊的栈来保存当前使用的各个页面的页面号
请求分段系统中,程序在运行之前,只需要调入少数几个分段(不必调入所有的分段)就可以启动运行。当所访问的段不在内存中时,可请求OS将所缺的段调入内存。
在请求分段式管理中所需要的主要数据结构是请求段表
每当发现程序要访问的断不再内存中,就有缺段中断机构产生一个中断信号,由OS将所需段调入内存。 在一条指令的执行期间产生和处理中断,可能发生多次中断,但不可能出现一条指令被分割在两个段中。
被访问的段不再内存时,需要地址变换。具体做法是先将所缺的段调入内存,并修改段表,然后利用段表进行地址变换。
记录了共享段的段号,段长,内存起始地址,状态(存在位),外村起始地址,共享进程计数(count)
当第一个使用共享段的进程提出请求时,由系统为该共享段分配一物理区,并调入该共享段,同时修改相应的段表(该段的内存地址)和共享段表,把 count 置为 1。当其它进程需要调用此段时,不需再调入,只需修改相应的段表和共享段表,再执行 count :=count+1 操作。
当共享共享段的某进程不再使用该共享段时,修改相应的段表和共享段表,执行 count :=count-1 操作。当最后一共享此段的进程也不再需要此段时,则系统回收此共享段的物理区,同时修改共享段表(删除该表项) 。
先利用段表寄存器中的段表长度与逻辑地址中的段号比较,若段号超界则产生越界中断。 再利用段表项中的段长与逻辑地址中的段内位移进行比较,若段内位移大于段长,也会产生越界中断。 注:在允许段动态增长的系统中,允许段内位移大于段长。
在段表中设置存取控制字段,用于规定对该段的访问方式。
环保护机构是一种功能较完善的保护机制。在该机制中规定:低编号的环具有高优先权。 OS 核心处于 0 环内;某些重要的实用程序和操作系统服务占居中间环;而一般的应用程序则被安排在外环上。 在环系统中,程序的访问和调用应遵循一定的规则:
同时在系统中运行的进程太多,由此分配给每一个进程的物理块太少,不能满足进程正常运行的基本要求,使得每个进程在运行时,频繁地出现缺页,必须请求系统调页,这样使得进程大部分时间用于页面置换,处理机效率急剧下降。
在一个请求分页存储管理系统中,进程P共有5页,页号为0至4。若对该进程页面的访问序列为3,2,1,0,3,2,4,3,2,1,0,4,试用最佳(Optimal)置换算法、LRU置换算法和FIFO置换算法,计算当分配给该进程的物理块数为3时,访问过程中发生的缺页次数和缺页率。
独占设备,进程互斥地访问这类设备,即系统一旦将该类设备分配给某进程,便由该进程独占,直到用完释放。
共享设备,指一段时间内允许多个进程同时访问的设备
一种按照字节交叉工作的通道,不适用于连接高速设备,通常含有许多非分配型子通道,每个子通道连接一个I/O设备,这些子通道按照时间片轮转方式共享主通道。
可以连接多台高速设备,但只含有一个分配型子通道,在一段时间内只能执行一道通道程序,控制一台设备进行数据传送,直到程序释放通道。利用率很低,但是传输效率很高。
数组多路通道结合了数组选择通道和字节多路通道,能使得各个子通道(设备)分时并行操作,含有多个非分配子通道。
用于实现CPU和设备控制器之间的通信,该接口含有三类信号线:数据线,地址线和控制线
用于连接一个或者多个设备
用于实现对设备的控制
在多道程序中,利用一道程序模拟脱机输入时的外围控制机功能,再利用另一道程序模拟脱机输出时外围控制机的功能,从而在主机的直接控制下,实现脱机输入输出。 在联机情况下实现的同时外围操作的技术,即为SPOOLing技术,假脱机技术。
指的是把磁头移动到指定磁道上花费的时间,该时间是启动磁头的时间sss和磁头移动nnn条磁道所花费的时间之和,即为 Ts=m∗n+sT_s=m*n+sTs=m∗n+s 其中m为一个常速,与磁盘驱动器的速度有关,一般磁盘,m为0.2;高速磁盘,m<=0.1
指的是指定扇区移动到磁头下所花费的时间 比如硬盘为15000 r/min,则每转需要4ms,从而平均旋转延时TτT_τTτ为2ms
指的是把数据从磁盘读出或者向磁盘写入数据所花费的时间,其大小和每次读写的字节数b和旋转速度有关: Tt=br∗NT_t=\frac{b}{r*N}Tt=r∗Nb 其中r为磁盘每秒的转速,N为一条磁道上的字节数 当一次读写的字节数相当于半条磁道上的字节数时,TtT_tTt和TτT_τTτ相同,因此,可将访问时间TaT_aTa表示为: Ta=Ts+12r+brNT_a=T_s+\frac{1}{2r}+\frac{b}{rN}Ta=Ts+2r1+rNb
根据进程请求访问磁盘的先后顺序进行调度
选择这样的进程,其要求访问的磁道与当前磁头所在磁道距离最近,使得每次的寻道时间最短,但是不能保证平均寻道时间最短
不仅考虑将要访问的磁道和当前磁道间的距离,更优先考虑的是磁头当前的移动方向。 比如,当磁头向外移动时,SCAN算法考虑的下一个磁道应该是当前移动方向上距离最近的,直到最后再反向移动
规定磁头单向移动,当磁头移动到最外磁道并访问后,磁头立即返回到最里面的欲访问磁道,即将最小磁道号和最大磁道号构成循环进行扫描
例题:假设一个磁盘的每个盘面上有200个磁道,现有多个进程要求对存储在该盘面上的数据进行访问,按照这些请求到达的先后次序,它们的访问位置分别处于54、57、39、18、90、162、154、38、180号磁道上。假定当前磁头在100号磁道上。请给出按“先来服务(FCFS)”、“最短寻道时间优先(SSTF)”和“扫描(SCAN)”算法进行磁盘调度时各自实际的访问次序,并计算它们的平均寻道长度。(注:当采用扫描算法时,规定当前磁头正在向磁道号增加的方向上移动)
解:
FCFS算法访问次序:54、57、39、18、90、162、154、38、180
平均寻道长度:55.3
SSTF算法访问次序:90、57、54、39、38、18、154、162、180
平均寻道长度:27.1
SCAN算法访问次序:154、162、180、90、57、54、39、38、18
平均寻道长度:26.8
数据项是最低级的数据组织方式,可分为基本数据项和组合数据项
记录是一组关于数据项的集合,用于描述一个对象在某方面的属性
文件是指由创建者所定义的,具有文件名的一组相关元素的集合,可分为有结构文件和无结构文件两种,每个文件都有文件属性。
文件属性
文件的逻辑结构,从用户观点出发看到的文件组织形式,即文件是由逻辑记录组成,是用户可以直接处理的数据及其结构,独立于文件的物理特性,又称为文件组织
文件的物理结构,又称为文件的存储结构,指系统将文件存储在外存上的存储组织形式
为了方便管理,含有三类信息
磁盘索引节点,每个文件有唯一的磁盘索引节点,包含内容
内存索引节点,存放在内存中的节点,文件打开时,将磁盘索引节点拷贝到内次索引节点中,包含内容
外存的组织形式分为连续组织方式,链接组织方式,索引组织方式
为每个文件分配一组相邻接的盘块 优点:
缺点:
为文件分配多个不连续的盘块,通过每个盘块上的链接指针将同属于一个文件的离散盘块形成链表,由此形成链接文件。
优点:
缺点:
链接方式:
为每个文件分配一个索引表(块),把分配给该文件的所有盘块号都记录在表(块)中,在建立一个文件时,只要在为之建立的目录项中填入指向改索引块的指针。
优点:
缺点:
为每个文件分配一个索引表(块),把分配给该文件的所有盘块号都记录在表(块)中,在建立一个文件时,只要在为之建立的目录项中填入指向改索引块的指针。对于一个大文件,所分配的块已满,必须再分配一个索引块。
优点:
缺点: