操作系统复习重点
老何操作系统复习重点整理
1.死锁的概念和死锁的原因、死锁的必要条件
----死锁的概念
死锁:是指两个或两个以上的进程在执行过程中,因争夺资源二造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。称此时系统处于死锁状态或系统产生了死锁。
称这些永远在互相等待的进程为死锁进程。
所占用的资源或者需要它们进行某种合作的其他进程就会相继陷入死锁,最终可能导致整个系统处于瘫痪状态。
共同点 | 区别 | |
---|---|---|
死锁 | 都是进程无法顺利向前推进的现象(故意设计的死循环除外) | 死锁一定是“循环等待对方手里的资源”导致的,因此如果有死锁现象,那至少有两个或两个以上的进程同时发生死锁。另外,发生死锁的进程一定处于阻塞态。 |
饥饿 | 可能只有一个进程发生饥饿。发生饥饿的进程既可能是阻塞态(如长期得不到需要的I/O设备),也可能是就绪态(长期得不到处理机) | |
死循环 | 可能只有一个进程发生死循环。死循环的进程可以上处理机运行(可以使运行态),只不过无法像期待的那样顺利推进。死锁和饥饿问题是由于操作系统分配资源的策略不合理导致的,而死循环是由代码逻辑的错误导致的。死锁和饥饿是管理者(操作系统)的问题,死循环是被管理者的问题。 |
----死锁的必要条件
产生死锁必须同时满足以下四个必要条件,缺一不可
互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的(因为进程不用阻塞等待这种资源)。
不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
请求和保持条件:进程已经保持了至少一个资源但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
注意:发生死锁时一定有循环等待,但是发生循环等待时不一定发生死锁,即,循环等待是死锁的必要不充分条件
如果同类资源数大于1,则即使有循环等待,也未必发生死锁。但如果系统中每类资源都只有一个,那循环等待就会导致死锁了。
----产生死锁的原因
1.对系统资源的进程。各进程对不可剥夺的资源的竞争可能引起死锁,对可剥夺的资源(CPU)的竞争是不会引起死锁的。
2.进程推进顺序非法。请求和释放资源的顺序不当,同样会导致死锁。例如,并发执行的进程P1、P2两者分别申请并占有了资源R1、R2,之后进程P1又紧接着申请资源R2,而进程P2又申请资源R1,两者会因为申请的资源被对方占有而阻塞,从而发生了死锁。
3.信号量的使用不当。如生产者-消费者问题中,如果实现互斥的P操作在实现同步的P操作之前,就有可能会导致死锁。(可以把互斥信号量、同步信号量看做是一种抽象的系统资源)
主要原因有两个:竞争临界资源和进程推进顺序不当。
总之,对不可剥夺资源的不合理分配,可能导致死锁
2.进程的基本状态分析
进程有三种基本状态:
1.就绪状态:进程已获得除CPU之外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就绪状态的进程排成一个就绪队列。
2.执行状态:进程已获得CPU,正在执行。单处理机系统中,处于执行状态的进程只有一个;多处理机系统中,有多个处于执行状态的进程。
3.阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进程执行受阻。(又称等待状态或封锁状态)
通常导致进程阻塞的典型事件有:请求I/O,申请缓冲空间等。
一般,还将处于阻塞状态的进程排成一个队列,有的系统还根据阻塞原因不同把这些阻塞集成排成多个队列。
3.利用信号量进行资源的同步和互斥操作伪程序设计
----信号量
----整型信号量
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量。
例如:某计算机系统中有一台打印机——
int S = 1; //初始化整型信号量S,表示当前系统中可用的打印机资源数量
void wait (int S) { //wait 原语,相当于“进入区”
while (S <= 0); //如果资源数不够,就一直循环等待
S = S-1; //如果资源数够,就占用一个资源
}
void signal (int S) { //signal原语,相当于“退出区”
S = S+1; //使用完资源后,在退出区释放资源
}
进程P0:
wait(S); //进入区,申请资源
使用打印机资源 //临界区,访问资源
signal(S); //退出区,释放资源
进程P1:
wait(S);
使用打印机资源
signal(S);
进程P2:
wait(S);
使用打印机资源
signal(S);
----记录型信号量
整型信号量的缺陷是存在“忙等”问题,因此又提出了“记录型信号量”,即用记录型数据结构表示的信号量
//记录型信号量的定义
typedef struct{
int value; //剩余资源数
struct process *L; //等待队列
} semaphore;
//某进程需要使用资源时,通过wait原语申请
void wait(semaphore S) {
S.value--;
if(S.value<0){
block(S.L);
/*如果剩余资源数不够,使用block原语使进程从运行态进入阻塞态,并挂到信号量S的等待队列(阻塞队列)中*/
}
}
//进程使用完资源后,通过signal原语释放
void signal(semaphore S) {
S.value++;
if(S.value<=0){
wakeup(S.L);
/*释放资源后,若还有别的进程在等待这种资源,则使用wakeup原语唤醒等待队列中的一个进程,该进程从阻塞态变为就绪态*/
}
}
信号量机制 | 整型信号量 | 用一个整数型变量作为信号量,数值表示某种资源数 |
---|---|---|
整型信号量与普通整型变量的区别:对信号量之鞥执行初始化、P、V三种操作 | ||
整型信号量存在的问题:不满足让权等待原则 | ||
记录型信号量 | S.value表示某种资源数,S.L指向等待该资源的队列 | |
P操作中,一定是先S.value--,之后可能需要执行block原语 | ||
V操作中,一定是先S.value++,之后可能需要执行block原语 | ||
注意:要能够自己推断在什么条件下需要执行block或wakeup | ||
可以用记录型信号量实现系统资源的“申请”和“释放” | ||
可以用记录型信号量实现进程互斥、进程同步 |
----信号量机制实现进程互斥
互斥信号量的初值实际上是标志某种资源的数量,而我们这里临界区同一时间段内值允许一个进程来访问,所以可以把临界区理解成一种特殊的资源,该资源只有一个,只能被分配给一个进程使用,只有这个进程释放了,才能被其他进程使用。如果一个进程需要使用临界区这种特殊的资源的时候,那么使用之前就应该对所对应的信号量进行P(通过)操作,之后进行V(释放)操作。
步骤:
1.分析并发进程的关键活动,划定临界区(如:对临界资源打印机的访问就应放在临界区)
2.设置互斥信号量mutex,初值为1
3.在临界区之前执行P(mutex)
4.在临界区之后执行V(mutex)
注意:对不同的临界资源需要设置不同的互斥信号量。
P、V操作必须成对出现。缺少P(mutex)就不能保证临界资源的互斥访问。缺少V(mutex)会导致资源永不被释放,等待进程永不被唤醒。
//信号量机制实现互斥
semaphore mutex = 1; //初始化信号量
P1(){
···
P(mutex); //使用临界资源前需要加锁
临界区代码段···
V(mutex); //使用临界资源后需要解锁
···
}
P2(){
···
P(mutex);
临界区代码段···
V(mutex);
···
}
//注意:对不同的临界资源需要设置不同的互斥信号量
----信号量机制实现进程同步
进程同步:要让各并发进程按要求有序地推进。
用信号量实现进程同步步骤:
1.分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码)
2.设置同步信号量S,初始为0;
3.在“前操作”之后执行V(S);
4.在“后操作”之前执行P(S);
//信号量机制实现同步
semaphore s = 0; //初始化同步信号量,值为0
p1(){
代码1;
代码2;
V(s);
代码3;
}
p2(){
P(s);
代码4;
代码5;
代码6;
}
//保证了代码4一定实在代码2之后执行
/*若先执行到V(S);操作,则S++后S=1。之后党执行到P(S)操作时,由于S=1,表示有可用资源,会执行S--,S的值变回0,P2进程不会执行block原语,而是继续往下执行代码4。
若先执行到P(S)操作,由于S=0,S--后S=-1,表示此时没有可用资源,因此P操作中会执行block原语,主动请求阻塞。之后当执行完代码2,继而执行V(S)操作,S++,使S变为0,由于此时有进程在该信号量对应的阻塞队列中,因此会在V操作中执行wakeup原语,唤醒P2进程,这样P2就可以继续执行代码4*/
4.银行家算法
----安全序列
若在某一时刻,系统能按照某种进程顺序,如{P1,P2,···,Pn},为每个进程分配其所需的资源,直至最大需求,使每个进程均可顺利完成,则称此时系统的状态为安全状态,称这样的一个进程序列{P1,P2,···,Pn}为安全序列。安全序列的实质是:序列中的每一个进程Pi(i=1,2,···,n)到运行完成尚需的资源量不超过系统当前剩余的资源量与所有在序列中排在它前面的进程当前所占有的资源量之和。
若在某一时刻,系统中不存在一个安全序列,则称系统处于不安全状态。
怎么解释?
——假设你是一位成功的银行家,手里掌握100亿资金(仅仅是假设)
有三个企业想找你贷款,分别是企业B,企业A,企业T,下面简称BAT。
B说,大哥,我最多跟你借70亿。
A说,大哥,我最多跟你借40亿。
T说,大哥,我最多跟你借50亿。
但是有个规矩:如果你借给企业的钱总数达不到企业提出的最大要求,那么不管之前借了多少,都拿不回来了。
刚开始,三个企业分别借了20,10,30亿。
最大需求 | 已借走 | 最多还会借 | |
---|---|---|---|
B | 70 | 20 | 50 |
A | 40 | 10 | 30 |
T | 50 | 30 | 20 |
你手里还剩40亿,此时B还想再借30亿,如果答应了,手里还剩10亿
最大需求 | 已借走 | 最多还会借 | |
---|---|---|---|
B | 70 | 20+30=50 | 50-30=20 |
A | 40 | 10 | 30 |
T | 50 | 30 | 20 |
这时候10亿已经小于三家公司最小的最多还会借的钱数,没有公司能够达到提出的最大要求,这样钱就打水漂了,上面就是不安全的
如果换一种:
此时A想借20亿,假如答应,手里还剩20亿,可以先给T,等T还钱,手里就有50亿了,再借给B,B还钱后就有70亿,最后给A。
当然按照A->T->B当然也是可以的,下面就是安全的
最大需求 | 已借走 | 最多还会借 | |
---|---|---|---|
B | 70 | 20 | 50 |
A | 40 | 10+20=30 | 30-20=10 |
T | 50 | 30 | 20 |
这样我们就会得到安全序列,不安全序列和死锁的关系了
所谓安全序列,就是指如果系统按照这种序列分配资源,则每个进程都能顺利完成,只要能找出一个安全序列,系统就是安全状态。当然,安全序列可以有很多个。
如果分配了资源以后,系统找不出任何一个安全序列,就进入了不安全状态。这就意味着之后可能所有进程都无法顺利地进行下去,当然如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总要考虑到最坏的状况。
如果系统处于安全状态,就一定不会发生死锁。如果系统处于不安全状态,不一定发生死锁。不安全状态未必就是发生了死锁,但发生死锁时一定是在不安全状态。
因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源的分配请求,这也是“银行家算法”的核心思想。
该算法用在操作系统中用于避免死锁
核心思想:在进程提出资源申请时,先预判此次分配是否会导致系统进入不安全状态,如果会进入不安全状态,就让该进程先阻塞等待。
----银行家问题的本质
要设法保证系统动态分配资源后不仅如此不安全状态,以避免可能产生的死锁。
即:每当进程提出资源请求且系统的资源能够满足该请求时,系统将判断如果满足此次资源请求,系统状态是否安全,如果判断结果为安全,则给该进程分配资源,否则不分配资源,申请资源的进程将阻塞。
银行家算法步骤:
1.检查此次申请是否超过了之前声明的最大需求数
2.检查此时系统剩余的可用资源是否还能满足这次请求。
3.试探着分配,更改各数据结构
4.用安全性算法检查此次分配是否会导致系统进入不安全状态
安全性算法步骤:
检查当前的剩余可用资源是否能满足某个进程的最大需求,如果可以,就把该进程加入安全序列,并把该进程持有的资源全部回收。
不断重复上述过程,看最终是否能让所有进程都加入安全序列。
银行家算法从避免死锁的角度上是非常有效的,但是从某种意义上说,它缺乏实用价值,因为很少有进程能够在运行前就知道其所需资源的最大值,而且进程数也不是固定的,往往在不断的变化,况且原本可用的资源也可能突然间不可用,因此在实际中,如果有,也只有极少的系统使用银行家算法来避免死锁。
5.作业调度基本算法
----先来先服务(FCFS)调度算法
FCFS调度算法是一种最简单的调度算法,该调度算法既可以用于作业调度也可以用于进程调度。在作业调度中,算法每次从后备作业队列中选择最先进入该队列的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。
在进程调度中,FCFS调度算法每次从就绪队列中选择最先进入该队列的进程,将处理机分配给它,使之投入运行,直到完成或因某种原因而阻塞时才释放处理机。
下面来一个实例,假设系统中有4个作业,它们提交时间分别为8、8.4、8.8、9,运行时间是2、1、0.5、0.2,采用FCFS调度算法。
作业 | 提交时间 | 运行时间 | 开始时间 | 等待时间 | 完成时间 | 周转时间 | 带权周转时间 |
---|---|---|---|---|---|---|---|
1 | 8 | 2 | 8 | 0 | 10 | 2 | 1 |
2 | 8.4 | 1 | 10 | 1.6 | 11 | 2.6 | 2.6 |
3 | 8.8 | 0.5 | 11 | 2.2 | 11.5 | 2.7 | 5.4 |
4 | 9 | 0.2 | 11.5 | 2.5 | 11.7 | 2.7 | 13.5 |
平均等待时间t=(0+1.6+2.2+2.5)/4=1.575
平均周转时间T=(2+2.6+2.7+2.7)/4=2.5
平均带权周转时间W=(1+2.6+5.4+13.5)/4=5.625
FCFS调度算法属于不可剥夺算法。从表面上看,它对所有作业都是公平的,但若一个长作业先到达系统,就会使后面许多短作业等待很长时间,因此它不能作为分时系统和实时系统的主要调度策略。但它常被结合在其他调度策略中使用。例如,在使用优先级作为调度策略的系统中,往往对多个具有相同优先级的进程按FCFS原则处理。
FCFS调度算法的特点是算法简单,但效率低;对长作业比较有利,但对短作业不利(相对SJF和高响应比);有利于CPU繁忙型作业,而不利于I/O繁忙型作业。
----短作业优先(SJF)调度算法
短作业优先调度算法是指对短作业优先调度的算法。从后备队列中选择一个或若干个运行时间最短的作业,将他们调入内存运行。而短进程优先(SPF)调度算法,则是从就绪队列中选择一个估计运行时间最短的进程,将处理机分配给他,使之立即执行,直到完成或发生某事件而阻塞时,才释放处理机。
如下表所示
作业 | 提交时间 | 运行时间 | 开始时间 | 等待时间 | 完成时间 | 周转时间 | 带权周转时间 |
---|---|---|---|---|---|---|---|
1 | 8 | 2 | 8 | 0 | 10 | 2 | 1 |
2 | 8.4 | 1 | 10.7 | 2.3 | 11.7 | 3.3 | 3.3 |
3 | 8.8 | 0.5 | 10.2 | 1.4 | 10.7 | 1.9 | 3.8 |
4 | 9 | 0.2 | 10 | 1 | 10.2 | 1.2 | 6 |
平均等待时间t=(0+2.3+1.4+1)/4=1.175
平均周转时间T=(2+3.3+1.9+1.2)/4=2.1
平均带权周转时间W=(1+3.3+3.8+6)/4=3.525
SJF调度算法也存在不容忽视的缺点:
1.该算法对长作业不利,由上表可以看出,SJF调度算法中长作业的周转时间会增加,更严重的是,如果有一长作业进入系统的后备队列,由于调度程序总是优先调度那些(即使是后进来的)短作业,将导致长作业长期不被调度,(“饥饿”现象,注意区分“死锁”,后者是系统环形等待,前者是调度策略问题)。
2.该算法完全未考虑作业的紧迫程度,因而不能保证紧迫性作业被及时处理
3.由于作业的长短只是根据用户所提供的估计执行时间而定的,而用户有可能会有意无意的缩短其作业的估计运行时间,致使该算法不一定能真正做到短作业优先调度。
注意:SJF调度算法的平均等待时间,平均周转时间最少。
----优先级调度算法
优先级调度算法又称优先权调度算法,该算法既可以用于作业调度,也可以用于进程调度,该算法中的优先级用于描述作业运行的紧迫程度。
在作业调度中,优先级调度算法每次从后备作业队列中选择优先级最髙的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。在进程调度中,优先级调度算法每次从就绪队列中选择优先级最高的进程,将处理机分配给它,使之投入运行。
根据新的更高优先级进程能否抢占正在执行的进程,可将该调度算法分为:
--非剥夺式优先级调度算法。当某一个进程正在处理机上运行时,即使有某个更为重要或紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于其自身的原因而主动让出处理机时(任务完成或等待事件),才把处理机分配给更为重要或紧迫的进程。
--剥夺式优先级调度算法。当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。
而根据进程创建后,其优先级是否能改变,可以将进程优先级分为以下两种:
--静态优先级。优先级是在创建进程是确定的,且在进程的整个运行期间保持不变。确定静态优先级的主要依据有进程类型、进程对资源的要求、用户要求
--动态优先级。在进程运行中,根据进程情况的变化动态调整优先级,动态调整优先级的主要依据为进程占有CPU时间的常盾、就绪进程等待CPU时间的长短。
----高响应比优先调度算法
高响应比优先调度算法主要用于作业调度,该算法是对FCFS调度算法和SJF调度算法的一种综合平衡,同时考虑每个作业的等待时间和估计的运行时间。在每次进行作业调度时,先计算后备作业队列中每个作业的响应比,从中选出响应比最高的作业投入运行。
响应比的变化规律可以描述为如下:
$$
响应比R{p}=\frac{等待时间+要求服务时间}{要求服务时间}
$$
根据公式可知:
--当作业的等待时间相同时,则要求服务时间越短,其响应比越高,有利于短作业
--当要求服务时间相同时,那么作业的响应比由其等待时间决定,等待时间越长,响应比就越高,所以实现的是先来先服务
--对于长作业,作业的响应比可以随等待时间的增加而提高,当其等待时间足够长时,其响应比便可很高,从而也就能获得处理机。克服了饥饿状态,兼顾了长作业。
----时间片轮转调度算法
时间片轮转调度算主要用于分时系统。在这种算法中,系统将所有就绪进程按照到达时间的先后次序排成一个队列,进程调度程序总是选择就绪队列中第一个进程执行,即先来先服务,但仅能运行一个时间片,比如100ms,在使用完一个时间片后,即使进程并未完成其运行,它也必须释放出处理机给下一个就绪的进程(被剥夺了),而被剥夺的进程返回就绪队列中的末尾重新排队,等候再次运行。
在时间片轮转调度算法中,时间片的大小对系统性能的影响很大,如果时间片足够大,所有进程都能在一个时间片内完成,那么这个算法就退化成了先来先服务调度算法,如果时间片过小,那么处理机就会在进程之间频繁切换,增大处理机的开销,真正用于运行用户进程的时间就会减少,因此需要选择恰当的时间片大小。
时间片的长短通常由以下因素确定:系统的相应时间、就绪队列汇总的进程数目与系统的处理能力。
----多级反馈队列调度算法
多级反馈队列调度算法是时间片轮转调度废和优先级调度算法的综合和发展,通过动态调整进程优先级和时间片大小,多级反馈队列调度算法可以兼顾多方面的系统目标。例如,为提高系统吞吐量和缩短平均周转事件而照顾短进程;为获得较好的I/O设备利用率和缩短响应时间而照顾I/O型进程;同时也不必事先估计进程的执行时间。
实现思想
1.设置多个就绪队列,并为各个队列赋予不同的优先级,第1级队列的优先级最高,第2级次之,逐级递减。
2.赋予各个队列中进程执行时间片的大小也各不相同,在优先级越高的队列中,每个进程的运行时间片就越小。例如,第2级队列的时间片比第1级长一倍······
3.当一个新进程进入内存后,首先将他放入第1级队列的末尾,按照FCFS原则排队等待调度,当轮到该进程执行时,如果它能在该时间片内完成,就可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第2级队列的末尾,再同样按照FCFS原则等待调度执行;如果在第2级队列的一个时间片内仍未完成,再用同样的方法到下一级队列,到最后一个队列后,按照时间片轮转的方式执行。
4.只有当第1级队列为空,调度程序才从第2级队列开始运行,(同理,当且仅当第 i-1 级队列为空,才会执行第 i 级队列)。如果正在执行第 i 级队列中的某进程时,又有新进程进入优先级较高的队列(1~i-1 任何一个),则此时新进程将抢占正在运行进程的处理机,由调度程序把正在运行的进程放回到第 i 级队列的末尾,把处理机交给新的更高优先级的进程。
多级反馈队列的优势:
· 终端型作业用户:短作业优先
· 短批处理作业用户:周转时间较短
· 长批处理作业用户:经过前面几个队列得到部分执行,不会长期得不到处理
6.页式虚拟存储管理机制的三种基本置换算法
----最佳淘汰算法 OPT
该算法每次都淘汰以后永不使用的,或者过最长的时间后才会被访问的页面
显然,这种算法会保证最低的缺页率,但它无法实现,因为它必须知道页面将来的访问情况,不过,这种算法可以作为衡量其他算法优劣的一个标准。
假定系统为某个进程分配了三个物理块,进程的访问顺序为7,0,1,2,0,3,0,4,2,3,0,3,2,1,2
采用OPT淘汰算法
7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 |
7 | 7 | 7 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
0 | 0 | 0 | 0 | 0 | 0 | 4 | 4 | 4 | 0 | 0 | 0 | 0 | 0 | |
1 | 1 | 1 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 3 | 1 | 1 |
----先进先出淘汰算法 FIFO
这种算法总是淘汰最先进入内存的页面,它实现简单,只需要把进程中已调入内存的页面,按照先后次序链成一个队列,并设置一个所谓的替换指针,使它总是指向内存中最老的页面。
缺点:效率不高,因为它与进程实际的运行规律不相适应,比如常用的全局变量所在的页面或者循环体所在页面都可能被选为淘汰对象,出现bleady现象。
Bleady现象:采用FIFO算法时,如果对一个进程未分配它所要求的的全部页面,优势就会出现分配的页面数增多,缺页率反而提高的现象。
成因:FIFO算法的置换特征与进程访问内存的动态特征是非常不一致的,即被置换的页面通常并不是进程不会访问的。
7 | 0 | 1 | 2 | 0 | 3 | 0 | 4 | 2 | 3 | 0 | 3 | 2 | 1 | 2 |
7 | 7 | 7 | 2 | 2 | 2 | 2 | 4 | 4 | 4 | 0 | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 | 3 | 3 | 3 | 2 | 2 | 2 | 2 | 2 | 1 | 1 | |
1 | 1 | 1 | 1 | 0 | 0 | 0 | 3 | 3 | 3 | 3 | 3 | 2 |
----最近最久未使用算法 LRU
根据页面调入内存后的使用情况,选择内存中最牛为使用的页面被置换,这是局部性原理的合理近似,性能接近最佳算法。
OPT算法使用页面将要被访问的时间,LRU算法使用页面最后一次被访问的时间。两者唯一的差别是:OPT是向前看的,LRU是向后看的。
LRU的实现算法:
1.计时法:每页加一个计时器,记录最后被访问的时间,淘汰时选最久的
2.堆栈法:每当访问,将页号从栈中移出,压入栈顶。
3.多位寄存器法:每页设置一个R位寄存器,每次昂文一页,将该页对应寄存器最左位置1,每隔一段时间右移一位,选择R值最小的淘汰
假设给某作业分配了三块主存,依次访问的页号是4,3,0,4,1,1,2,3,2。当访问这些页时,页面淘汰序列变化情况如下
访问页号 | 页面淘汰序列 | 被淘汰页面 |
---|---|---|
4 | 4 | |
3 | 4 3 | |
0 | 4 3 0 | |
4 | 3 0 4 | |
1 | 0 4 1 | 3 |
1 | 0 4 1 | |
2 | 4 1 2 | 0 |
3 | 1 2 3 | 4 |
2 | 1 3 2 |
LRU的开销是很大的,必须有硬件的支持,完全由软件实现会极大降低速度。
最先:先找到先分配
最佳:找最小的且可满足的
最坏:找最大的
7.计算访问一个物理单元的时间,这里牵涉到TLB的访问时间,页表的访问时间,虚拟地址和物理地址的转换,内存访问时间的概念
----TLB
TLB是Translation Lookaside Buffer的简称,可以翻译为“地址转换后援缓冲器”,也可简称为“快表”。
因为页表是存放在内存中的,CPU要存取一个数据,需要访问主存两次
第一次:访问内存中的页表,找到该页的物理块号,将此块号与页内地址拼接形成物理地址。
第二次:真正访问该物理地址,存取其中的内容。
这样就降低了程序的执行速度
为了提高存取速度,在地址变换机制中增设一组寄存器,用来存放访问的那些页表
快表是一种访问速度比内存快很多的高速缓冲器,也可称联想存贮器(TLB)
当进程访问一页时,系统将页号与快表中的所有项进行比较,若访问的页在快表中,即可立即进行地址转换。
当访问的页不在快表中,去内存中查询页表,同时将页表找到的内存块号与虚页号填入快表中
例题:
假设快表命中率是98%,访问时间是10ns,内存访问时间是100ns,求平均访问时间?
平均访问时间=98%(10+100)+(1-98%)(10+100+100)
解析:
若快表命中:
TLB检索时间:10ns;
访问内存一次取得数据时间:100ns;
总时间:110ns
若快表未命中:
TLB检索时间:10ns;
访问内存一次检索页表时间:100;
访问内存一次取得数据时间:100ns;
总时间:210ns
----地址映射:地址转换
在系统中设置地址变换机构,能将用户进程地址空间中的逻辑地址变为内存空间中的物理地址
由于页面和物理块的大小相等,页内便宜地址和块内偏移地址是相同的。无须进行从页内地址到块内地址的转换。
地址变换机构的任务,关键是将逻辑地址中的页号转换为内存中的物理块号。物理块号内的偏移地址就是页内偏移地址。
页表的作用就是从页号到物理块号的转换,所以地址变换的任务借助于页表来完成
地址变换过程:
1.根据逻辑地址计算出页号、页内偏移量
2.页号的合法性检查(与页表长度对比)
3.若页号合法,再根据页表起始地址、页号找到对应页表项(这里进行第一次页表访问)
4.根据页表项中记录的内存块号、页内偏移量 得到最终的物理地址
5.访问物理内存对应的内存单元(这里进行第二次页表访问,存取数据)
重点:
如果题目中用十进制表示逻辑地址:
页号=逻辑地址/页面长度(整除)
页内偏移量=逻辑地址%页面长度(取余)
例题:
有一系统采用页式存储管理,有一作业大小是8KB,页大小为2KB,依次装入内存的第7,9,10,5块,试将虚地址7165,3412转换成内存地址
页号 | 块号 |
---|---|
0 | 7 |
1 | 9 |
2 | 10 |
3 | 5 |
虚地址3412:
P=3412 % 2048 = 1 //得到页号为1
W=3412 mod 2048 = 1364 //页内偏移量为1364
MA = 9 * 2048 + 1364 = 19796 //为什么是9*2048?因为页号1所对应的内存块号是9
则虚地址3412对应的内存地址为19796
虚地址7145:
P=7145 % 2048 = 3 //得到页号为3
W=7145 mod 2048 = 1001 //得到页内偏移量是1001
MA = 5 * 2048 + 1001 = 11241
则虚地址7145对应的内存地址为11241
在分页存储管理(页式管理)的系统中,只要确定了每个页面的大小,逻辑地址结构就确定了。因此,页式管理中地址是一维的。即,只要给出一个逻辑地址,系统就可以自动的算出页号、页内偏移量 两个部分,并不需要显式地告诉系统这个逻辑地址中,页内偏移量占多少位。
8.外部设备缓冲区的原理及计算
----缓冲区的引入
引入原因:
1.缓和CPU与I/O设备间速度不匹配的矛盾
2.减少对CPU的中断频率,放宽对CPU中断响应时间的限制
3.解决数据粒度不匹配的问题
4.提高CPU和I/O设备之间的并行性
----单缓冲区
每当用户进程发起一次I/O请求,系统就在主存中分配一个缓冲区。假定从磁盘把一块数据输入到缓冲区的时间为T,OS将该缓冲区中的数据传送到用户区的时间为M,而CPU对这一块数据处理计算的时间为C。由于T和C是可以实现并行的,所以当T>C时,系统对每一块数据的处理时间为M+T,反之则为M+C,故可以把系统对每一块数据处理时间表示为Max(C,T)+M.
简单的理解:单缓冲区的总体运行时间 = (磁盘块-1) * MAX(C,T) +磁盘块 * M + C + T (两端没有重合的地方)
例题:
文件占38个磁盘块,设有一个缓冲区,磁盘块大小相同,一个磁盘块读入缓冲区的时间是230μs,将缓冲区数据送到用户区的时间是10μs,读入并处理完该文件的时间是18090μs,单缓冲区结构,求CPU对一块数据进行处理的时间
解:已知磁盘块数,T,M,总时间,求C
根据单缓冲区工作原理示意图(书本P225)
得 37 * Max(C,230) + 38 * 10 + C + 230 = 18090
若C<=230,得到的数据很明显不对
C>230,求得C=460μs
----双缓冲区
为了加快输入输出速度,引入双缓冲机制,也称为缓冲对换。设备输入时,先送入第一缓冲区,装满后转向第二缓冲区。此时系统可从第一缓冲区移出数据,送入用户进程,接着Cpu对数据进行计算。在双缓冲时,系统处理一块数据的时间可以粗略地认为是Max(C,T),如果考虑M,则处理一块数据的时间为Max(C+M,T) (更准确的:Max(C,T-M)+M)如果C<T,可使块设备连续输入,如果C>T,则可使CPU不必等待设备输入。
简单的理解:双缓冲区的总体运行时间 = (磁盘块 - 1) * Max(T,M+C) + T + M + C (两端没有重合的地方)
例题:
一个文件占34个磁盘块,设一个缓冲区与磁盘块大小相同,将缓冲区的数据传送到用户区的时间是50μs,CPU对一块数据进行处理的时间为180μs,读入并处理完该文件的时间是12810μs,在双缓冲区的结构下,求把一个磁盘块读入缓冲区的时间
解:已知磁盘块数,M,C,总时间,求T
根据双缓冲区工作原理图(书本P226)
得 33 * Max(T,180+50) + T + 50 + 180 = 12810
同上,最终求得T = 370μs
----环形缓冲区
当输入与输出的速度基本相匹配时,采用双缓冲能获得较好的效果,可使生产者和消费者基本上能并行操作。但当两者的速度相差很远时,双缓冲的效果很不理想,因此引入环形缓冲区
在环形缓冲区中包括多个缓冲区,每个缓冲区的大小相同,作为输入的多缓冲区可分为三类:用于装输入数据得空缓冲区,已装满数据的缓冲区G,计算进程正在使用的现行工作缓冲区C。
----缓冲池
既可以用于输入也可以用于输出的公用缓冲池,在池找那个设置了多个可供若干个进程共享的缓冲区
缓冲池和缓冲区的区别:缓冲区仅仅是一组内存块的链表,而缓冲池则是包含了一个管理的数据结构以及一组操作函数的管理机制,用于管理多个缓冲区。
9.磁盘寻道的四种基本调度算法(扫描算法也叫电梯算法)
磁盘寻道有四种常见算法:
1.先来先服务算法(FCFS)
2.最短寻道时间优先算法(SSTF)
3.扫描算法(SCAN)(也称电梯算法)
4.循环扫描算法(CSCAN)
----先来先服务算法(FCFS)
根据进程请求访问磁盘的先后顺序进行调度,此算法的优点是公平、简单,且每个进程的请求都能依次得到处理,不会出现某一进程的请求长期得不到满足的情况。但此算法由于未对寻道进行优化,致使平均寻道时间可能较长
初始位置 | 100 |
---|---|
磁道编号 | 移动距离 |
55 | 45 |
58 | 3 |
39 | 19 |
18 | 21 |
90 | 72 |
160 | 70 |
150 | 10 |
38 | 112 |
184 | 146 |
平均寻道长度 | 55.3 |
与接下来的几种调度算法相比,其平均寻道长度较大,故GCGS算法仅仅适用于请求磁盘I/O的进程数目较少的场合。
----最短寻道时间优先(SSTF)
要求访问的磁道与当前磁头所在的磁道距离最近,以使每次的寻道时间最短。但这种算法不能保证平均寻道时间最短。可以得到比较好的吞吐量,但不猛保证平均寻道时间最短。
初始位置 | 100 |
---|---|
磁道编号 | 移动距离 |
90 | 10 |
58 | 32 |
55 | 3 |
39 | 16 |
38 | 1 |
18 | 20 |
150 | 132 |
160 | 10 |
184 | 24 |
平均寻道长度 | 27.5 |
对用户的服务请求的响应机会不是均等的,因而导致响应时间的变化幅度很大。在服务请求很多的情况下,对内外边缘磁道的请求将会无限期地被延迟,有些请求的响应时间将不可预期。SSTF算法虽然能获得较好的寻道性能,但却可能导致某个进程发生“饥饿”现象,因为只要不断有新进程的请求到达,且其所哟访问的磁道与磁头当前所在磁道的距离较近,这种新进程的I/O请求必然优先满足。
----扫描算法(SCAN)(电梯算法)
不仅考虑到欲访问的磁道与当前磁道间的距离,更优先考虑的是磁头当前的移动方向。例如,当磁头正在自里向外移动时,SCAN算法所考虑的下一个访问对象,应是其欲访问的磁道既在当前磁道之外,又是距离最近的,这样自里向外的访问,直至再无更外的磁道需要访问时,才将磁臂换向为自外向里移动。这时,同样也是每次选择这样的进程来调度,即要访问的磁道在当前位置内距离最近者,这样,磁头又逐步的从外向里移动,直至再无更里面的磁道要访问,从而避免了饥饿现象的发生
循环往复
初始位置 | 100 |
---|---|
磁道编号 | 移动距离 |
150 | 50 |
160 | 10 |
184 | 24 |
90 | 94 |
58 | 32 |
55 | 3 |
39 | 16 |
38 | 1 |
18 | 20 |
平均寻道时间 | 27.5 |
由于在这种算法中磁头移动的规律颇似电梯的运行,因而又常称之为电梯调度算法
----循环扫描算法(CSCAN)
SCAN算法既能获得较好的寻道性能,又能防止饥饿现象,故被广泛使用,但也存在这样的问题,当磁头刚刚从里向外移动而越过了某一磁道时,恰好又有一进程请访问此磁道,这是,此进程必须等待,待磁头继续从里向外,然后再从外向里扫描完所有要访问的磁道后,才处理该进程的请求,致使该进程的请求被大大的推迟。
为了减少这种延迟,CSCAN算法规定磁头单向移动,例如只是从里向外移动,当磁头移动到嘴歪的磁道并访问后,磁头立即返回到最里的欲访问的磁道,亦即将最小磁道号紧接着最大磁道号构成循环,进行循环扫描
采用循环扫描方式后,上述请求进程的请求延迟将从原来的2T减为T+Smax,其中,T为由里向外或由外向里单项扫描完要访问的磁道的寻道时间,而Smax是将磁头从最外面被访问的磁道直接移动到最里面欲访问的磁道的寻道时间。
Copyright ©2022 顾志豪 All rights reserved.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 分享4款.NET开源、免费、实用的商城系统
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 5. Nginx 负载均衡配置案例(附有详细截图说明++)