(只写一些重要的点)
1.操作系统是一组能有效地组织和管理计算机硬件和软件资源,合理地对各类资源进行调度,以及方便用户使用的程序的集合。
2.三种基本操作系统是多道批处理系统、分时系统、实时系统。
3.实现进程同步之记录型信号量机制:
遵循让权等待准则,即消除了忙则等待的现象。
数据项结构体如下:
typedef struct
{
int value;
struct process_control_block *list; //阻塞进程指针
} semaphere;
两个原子操作P(wait)、V(signal)描述如下:
wait(semaphere *S)
{
S->value--;
if(S->value < 0) block(S->list); //进程未获取资源则自我阻塞
}
signal(semaphere *S)
{
S->value++;
if(S->value <= 0) wakeup(S->list); //释放资源后发现有阻塞进程则唤醒
}
S->value初值即资源数目,=1是转化为互斥信号量。
4.经典同步问题之生产者消费者问题:
利用记录型信号量解决:①利用互斥信号量mutex实现进程对缓冲池(同一时间仅允许进行生产或者消费,不能同步进行)的互斥访问。②利用资源信号量empty和full表示空缓冲区和满缓冲区的数量进行同步访问。
int in = 0, out = 0; //对缓冲区进行循环使用
int count = n; //记录空缓冲区的数量
item buffer[n];
seamphere mutex = 1, empty = n, full = 0;
void producer() //生产者
{
do
{
...
wait(empty);
wait(mutex);
buffer[in] = nextp;
in = (in+1)%n;
++count;
signal(mutex);
signal(full);
}while(true);
}
void consumer() //消费者
{
do
{
wait(full);
wait(mutex);
nextp = buffer[out];
out = (out+1)%n;
--count;
signal(mutex);
signal(empty);
...
}while(true);
}
实现互斥访问的wait(mutex)和signal(mutex)必须成对出现。资源信号量也需要成对出现,只不过可以分别处于不同的进程中。执行wait()操作时,互斥信号量和资源信号量的顺序不能颠倒(容易产生死锁):(假如顺序颠倒)当所有缓冲池都被生产满了的时候,尝试进行生产者操作,先执行互斥资源信号量的wait可能成功,然后进程一直在等待执行同步信号量的wait操作,但是已知达到满的情况了,此操作会一直不成功,从而导致互斥资源一直被占用无法释放,进而导致消费者对互斥资源的wait操作不能成功,造成死锁。
5.几种作业调度算法
①先来先服务调度算法(FCFS)
其实就是按照队列的思想进行调度,优先考虑在系统中等待时间最长的作业,不管该作业所需执行时间的长短,将其调入内存运行。
②短作业优先调度算法(SJF)
作业长短(作业运行时间)来计算优先级,作业越短越先被调入内存。缺点:必须预知作业的运行时间,还会可能致使长作业等待。
时间过长,出现饥饿现象。
③优先级调度算法(PSA)
如名,为每个作业设置一个优先级然后进行调度。
④高响应比优先级调度算法(HRRN)
为每个作业设置一个动态优先级,随着等待时间的延长优先级会增加,这样可以致使长作业的优先级在等待期间不断增加,避免了饥饿现象。优先权 = (等待时间+要求服务时间)/要求服务时间,由于等待时间+服务时间就是系统对该作业的响应时间,故该优先级有相当于响应比Rp,故优先权又可表示为
Rp =(等待时间+要求服务时间)/要求服务时间 = 响应时间/要求服务时间。
6.进程调度算法:轮转调度算法(RR)
在分时系统中,最常用和简单的就是基于时间片的轮转调度算法,采用FCFS策略,将所有就绪进程排成一个就绪队
列,设置每隔一定时间间隔产生一次中断,并激活调度程序将CPU分配给队首进程。即让就绪队列上的每个进程每次
仅运行一个时间片。
进程切换时机:①一个时间片尚未用完,正在运行的进程已经完成,就立即激活调度程序,将该进程在就绪队列删
除,再调度就绪队列的队首进程,并启动一个新的时间片。②一个时间片被用完,计数器中断处理程序被激活。
一个较为可取的时间片大小是略大于一次典型的交互所需要的时间,使大多数交互式进程能在一个时间片内完成。
下图是时间片q=1和q=4时对平均周转时间的影响。
(下图q=1有错,更正请看评论)
7.实时任务的调度
实时系统中可能存在两种不同性质的实时任务:HRT任务和SRT任务,它们都联系着一个截至时间。实现实时调度,需要系统具有相应的系统处理能力。假定系统中存在m个周期性硬实时任务HRT,它们处理时间为Ci,周期时间为Pi,则在有N个处理机的系统中需要满足下面限制条件系统才是可调度的。
实时调度算法分为非抢占式调度算法和抢占式调度算法。
非抢占式调度算法包括:非抢占式轮转调度算法;非抢占式优先调度算法。
抢占式调度算法包括:基于始终中断的抢占式优先级调度算法;立即抢占的优先级调度算法。
下面再看两种优秀调度算法:
①最早截至时间优先算法(EDF)
首先是非抢占式调度方式用于非周期实时任务:概括一下就是,几个非周期任务先后到达,若是在一个任务执行期间,到达了多个任务,则选择一个最早的开始截止时间的任务调度执行。
其次是抢占式调度方式用于周期实时任务:
假设存在两个周期任务,任务A和任务B的周期时间分别是20ms和50ms,每个周期的处理时间分别是10ms和20ms。首先需要分析系统处理能力:10/20+25/50=1满足<=1,所以对于单处理机能够承受。对于周期性任务,它们的截至时间即是下一个周期的开始时间。调度:截至时间越早的任务优先级越高,当新任务到达时,若优先级比当前执行的任务高,进行抢占。例子如图。
②最低松弛度优先算法(LLF)
该算法在确定任务的优先级时,根据的是任务的紧急(或松弛)程度。任务紧急程度越高(即松弛时间越短)其优先级越高。
松弛度 = 完成截至时间-任务的执行时间-当前时间。
实现该算法要求系统中有一个按松弛度排序的实时任务就绪队列。该算法主要用于可抢占调度方式中
假设存在两个周期任务,任务A和任务B的周期时间分别是20ms和50ms,每个周期的处理时间分别是10ms和20ms。系统处理能力同上。
在刚开始时,A1和B1同时到达,A1的松弛度为10ms,B1的松弛度为25ms,所以先执行A1。执行完A1之后,A2还未到达,所以只能执行B1。当A2在t=20ms时到达时,计算得其松弛度为10ms不触发抢占。但当t=30ms时,B1还未执行完,而此时A2的松弛度变成0ms,此时触发抢占,调度程序抢占B1的处理机调度A2运行。
后面的执行也是如此...即一个后续程序获得处理机资源的时机为:
①当前任务执行完成,而此时该后续任务的松弛度最低,获得处理机资源。
②在当前任务执行过程中,该后续任务的松弛度变为0,获得最高优先权,立即抢占CPU。
Gant图如下:
8.利用银行家算法避免死锁--神犇Dijkstra
每当一个新进程进入系统时,必须申明在运行过程中所需要每种资源类型的最大单元数目,其数目不应超过系统所拥有的资源资源。当进程请求一组资源时,系统必须首先确定是否有足够的资源分配给该进程。若有,再进一步计算将这些资源分配给该进程后,是否会使系统处于不安全状态。如果不会才将资源分配给它,否则让其等待。即银行家算法始终保持系统处于安全状态。
四个数据结构分别描述:系统中可利用的资源(Avaliable)、所有进程对资源的最大需求(Max)、系统的资源分配(Allocation)、所有进程还需多少资源(Need)。
银行家算法步骤:
假设共n个进程,m个资源。设Requesti是进程Pi的请求向量,如果Requesti[j]=K,表示进程Pi需要K个Rj类型的资源。当Pi发出资源请求后,系统按照以下步骤进行检查:
1):如果所有Requesti[j]<=Need[i,j](1<=j<=m),执行B,否则认为出错,因为它需要的资源数已经超过它所宣布的最大值。
2): 如果所有Requesti[j]<=Available[j](1<=j<=m),转向步骤C,否则尚无足够资源,Pi必须等待。
3):系统试探着把资源分配给进程Pi,并修改下面数据结构中的数值:
Available[j] = Available[j] - Requesti[j];
Allocation[i,j] = Allocation[i,j] + Requesti[j];
Need[i,j] = Need[i,j] - Requesti[j];
4)系统执行安全性算法,检查此次自奥运分配后系统是否处于安全状态。若安全,才正式将资源分配给进程Pi,以完成本次分配,否则,将本次的试探分配作废,恢复原来的资源分配状态,让进程Pi等待。
安全性算法步骤:
1)设置两个向量:① 工作向量Work:它表示系统可提供给进程继续运行所需的各类资源数量的多少,它含有m个元素,在执行安全算法开始时,Work = Available;② Finish:它表示系统是否有足够的资源分配给进程,使之运行完成。开始时先将所有的Finish[i]置为 false;当有足够资源分配给进程时, 再令Finish[i]= true。
2)从进程集合中找到一个能满足下述条件的进程:
① Finish[i]= false; ② Need[i,j]≤ Work[j];
如果找到,那么,执行步骤(3);否则,执行步骤(4)。
3)当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
Work[j]= Work[i]+Allocation[i,j];
Finish[i]= true;
go to step 2;
4)如果所有进程的Finish[i]= true都满足, 则表示系统处于安全状态;否则,系统处于不安全状态。
对五个进程三类资源,各类资源数量分别为10、5、7在某个时刻的分配情况,以及对它的安全性检查。如下两图:
所有的finish[i]都变为了true,所以此时系统是处于安全状态的。
9.离散分配方式之分页存储管理方式
页面:进程的逻辑地址空间分为若干个页面,从0编号。
页块:内存的物理地址空间分为若干个页块,从0编号。
地址结构:分解32位地址长度,0~x位表示位偏移量W即页内地址,x+1~31为页号。
假如x=11,则0~11位为页内地址,即每个页面的大小为4KB,12~31位为页号,即地址空间最多允许有1M个页面。对于某特定机器,给定一个逻辑地址空间中的地址A,页面的大小为L,则页号P和页内地址d用下式求得。
P = INT[A/L];(整除) d = A mod L;(取余)
页表:页面映像表,一个页表项记录了相应页在内存中对应的物理块号。
页表的作用式实现从页号到物理块号的地址映射。
地址变换机构,即将用户地址空间中的逻辑地址转换为内存空间中的物理地址。
①.基本的地址变换机构:
如图,首先根据逻辑地址计算出页号,然后将页号和页表长度进行比较,如果页号大于等于页表长度,则表示本次访问的地址已超越了进程的地址空间,产生一次越界中断。若未出现错误,则将页表始址与页号和页表项长度(页表中每一个数据项占用的内存大小)的乘积相加,得到该表项在页表中的位置,于是对应得到了该页的物理块号,然后将之装入物理地址寄存器中,同时通过逻辑地址计算出页内地址并送入物理地址寄存器的块内地址字段(即此逻辑地址的物理地址为物理块号b乘以页面/页块大小L再加上页内/块内地址)。
②.具有快表的地址变换机构
由于页表是存放在内存中的,这使CPU在每存取一个数据时,都要两次访问内存。
所以为了提高地址变换速度,可在地址变换机构增设一个具有并行查寻能力的特殊高速缓冲寄存器,又称为"联想寄存器",或称为"快表"。
地址变换过程:在CPU给出有效逻辑地址后,由地址变换机构自动地将页号P送入高速缓冲寄存器,并将此页号与高速缓存中所有页号进行比较,若其中有与此相匹配的页号,便表示所要访问的页表项在快表中。于是,可直接从快表中读出该页所对应的物理块号,并送到物理地址寄存器中。若在快表中未找到对应的页表项,则还须再访问内存中的页表,找到后,把从页表中读出的物理块号送往物理地址寄存器;同时,再将此页表项存入快表中的一个寄存器单元中,亦即,重新修改快表。如果联想寄存器已满,则OS必须找到一个老的且已被认为是不再需要的页表项,将它换出。
10.