第一部分 进程管理
1 进程和线程
1.1 进程
一个进程就是一个正在运行的程序,是资源分配的基本单位,包含以下几个方面的内容:
- 程序块:即对应的程序的代码和数据;
- 进程控制块(CPU上下文):程序在运行时,CPU各种寄存器的当前值,如PC、PSW、栈指针寄存器等。
- 数据块(一组系统资源):包含操作系统用来管理进程的数据结构、进程的内存地址空间、进程正在使用的文件等。
总之,进程是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的一个独立单位。
进程和程序的区别
- 进程是程序的一次执行过程,没有程序就没有进程;
- 程序是完成某个特定功能的一系列程序语句的集合,只要不被破坏就可以永远存在;
- 程序是一个静态的概念,而进程是一个动态的概念,创建后产生,任务结束后就消失。
1.2 进程和线程
线程,就是进程中的一条执行流程,是程序执行的基本单位。进程是一个正在运行的程序,它把一组相关的资源整合起来构成一个资源平台,包括运行上下文、内存地址空间、打开的文件等,线程就可以看作是代码在这个资源平台上的一条执行流程。
对于不同的线程,它们运行在同一个资源平台上,可以共享所在进程的各种资源,同时每个线程又有自己独立的CPU运行上下文(各种寄存器)和栈。如下图单线程的程序,上面表示它拥有的资源,包括代码、数据、文件、寄存器和栈,下面是它的一个线程;而多线程的进程中,代码、数据和文件是进程一级的资源,被所有的线程共享,只有寄存器和栈是线程独有的。
2 三态模型(★★★)
在多道程序系统中,任务是独立运行的实体,需要参与到系统资源的竞争,只有在所需的资源都得到满足的情况下才能在CPU上运行。因此,任务所拥有的资源情况是不断变化的,导致任务的状态也在不断变化。一般来说,任务会具备以下三种基本状态:
- 运行状态(running):任务占用CPU,并在CPU上运行。运行态的任务小于等于CPU数目。
- 就绪状态(ready):任务已经具备了运行的条件,但由于CPU正忙,所以暂时不能运行。
- 阻塞状态(blocked):也叫等待状态(waiting)。任务因为正在等待某事件发生暂时不能运行。
如图所示,三态模型中任务的状态存在4种转换关系:
- 运行→阻塞:正在运行的任务由于等待某个事件而被阻塞。例如程序需要等待用户输入;
- 阻塞→就绪:一个任务曾经等待某个事件的发生而被阻塞,等待的事件发生,则任务从阻塞态变为就绪态;
- 就绪→运行:处于就绪状态的任务被调度器选中去运行;
- 运行→就绪:一个任务正在CPU上运行,由于某种原因(该任务的时间片用完或者有更高优先级的任务就绪),调度器选择另外一个任务去运行,当前任务就从运行态变为就绪态。
任务在不同状态之间的转换完全是自动进行的,是由操作系统完成的。
操作系统通过任务控制块(Task Control Block, TCB)来设计和实现任务机制,任务管理就是通过对各个任务的TCB操作来实现的。TCB主要包括了以下内容:
- 任务的管理信息:任务标识ID、状态、优先级、调度信息、时间统计信息等;
- CPU上下文信息:各种CPU寄存器的当前值;
- 资源管理信息:一些资源管理方面的信息,存储管理和文件管理相关信息。
了解 七态模型:
实际应用中,任务还会有创建和结束的状态。为了解决内存占用问题,有时还会将任务挂起,于是就有了七态模型。
挂起:将进程调出内存,保存到外存队列中,并释放资源;
激活:恢复挂起的进程,重新调入内存。
五态模型有两种说法,了解即可:其中运行、阻塞、就绪加上创建和结束可以组成五态模型;运行、阻塞、就绪加上就绪挂起和阻塞挂起又可以构成另一种五态模型。
3 PV操作(★★★)
3.1 前趋图
前趋图是用于描述程序执行顺序的一个有向无循环图,由节点和有向边构成;
节点可以表示一个语句、一个程序段或一个进程;
节点间的有向边表示两个节点之间存在前趋关系(即程序执行的先后顺序关系)。对于一个节点,以该节点为起点有向边的个数称为出度,以该节点为中点的有向边的个数称为入度。
下图使用前趋图表示包饺子的过程,其中ABC出度为1,D入度为3,出度为1,E入度为1。
3.2 同步与互斥
同步:为了完成某种工作而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待或传递消息产生的制约关系。(同步是一种协同关系,前面的操作没有完成,后面的操作也不能进行)。
互斥:指一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一个进程才被允许去访问此临界资源。(互斥是一种竞争关系,一个操作进行,其他的操作就必须等待)。
3.3 PV操作(★★★)
临界资源:多个进程可以共享系统中的各种资源,其中很多资源一次只能为一个进程所使用,把一次仅允许一个进程使用的资源称为临界资源。
临界区:在每个进程中,访问临界资源的那段代码或进程称为临界区。对临界资源的访问必须互斥地进行。
信号量:信号是一种有效的进程同步互斥的工具。信号量的数据结构是一个值和一个指针,指针指向等待该信号量的进程。S>=0时表示可用资源的个数,S<=0时表示S等待队列中进程的个数。两个进程时,S取值范围是[-1,1];n个进程时,S的取值范围是[1-n, 1]
P操作可以理解为请求一个资源,即S-1,若S减1后仍大于0,说明还有资源可用,则请求进程可以继续执行;若S减1后小于0,说明没有资源可用,进程被阻塞后进入信号对应的队列中,然后转进程调度。
V操作可以理解释放一个资源,即S+1,若S+1后大于0,说明还有资源可释放,则请求的进程可以继续执行;若S减1后小于等于0,说明这个信号的等待队列中有阻塞的进程,就唤醒一个等待的进程,然后再返回到原进程继续执行或转进程调度。
3.4 PV原语
单缓冲区 生产者-消费者 PV原语描述:
3.5 示例
前趋图中的信号量S的排序规则:从上到下、从左到右、起点相同、比较终点。
3.5.1 PV操作和前趋图
进程P1、P2、P3、P4和P5前趋图如图所示
若用PV操作控制5个进程的并发执行过程,需要设置5个信号量S1,S2,S3,S4,S5,且信号量的初值均为0,则右图中a和b分别应该填 (1) ; c和d分别填 (2) ;e和f分别填 (3) ;
解析:在前趋图中标出信号量,如下图所示,则
a为V(S1)和V(S2),b为V(S3),
c为P(S2),d为V(S4),
e为V(S5),f为P(S4)和P(S5)。
3.5.2 顾客和店员问题
某商店有两名发货员,一名审核员。当顾客提货时,如果发货员空闲,就允许顾客提货,顾客离开时,审核员审核顾客提货是否正确。工作流程如图所示,为了使用PV操作来协调他们的工作,设置两个信号两S1和S2,S1初值为2,S2初值为1。则图中a,b,c,d应填____。
解析:
S1初值为2,表示发货员空闲数;
S2初值为1表示审核员空闲数;
顾客首先提货,申请一个发货员资源,即P(S1);
提完货以后释放发货员资源,即V(S1);
顾客离开前由审核员审核,先申请一个审核员资源,P(S2);
检验完成以后释放审核员资源,即 V(S2);
则答案为:P(S1), V(S1), P(S2), V(S2)
4 死锁
4.1死锁概念
进程管理是操作系统的核心,如果设计不当,出现了一个进程在等待一个不可能发生的事件,则会出现死锁。
系统有n个进程,每个进程需要m个资源,当系统有n*(m-1)+1个资源时,则不可能发生死锁。例如:系统中有3个进程,每个进程都需要4个资源,考虑最坏的情况,每个进程都占有3个资源,这时候再多一个资源(共3*(4-1)+1 = 10个资源),就保证有一个进程可以运行,不会发生死锁。
4.2 死锁解决(★★)
4.2.1 产生死锁的条件
死锁的产生需要满足以下4个条件:
条件互斥:一个资源每次只能由一个进程使用,如果有其他进程请求该资源,需要等待占用资源的进程用完释放。
请求与保持:一个进程已经占有了一个或多个资源,但又请求新的资源,而新的资源被其他的进程占有,此时该进程阻塞,原本占有的资源也保持不放;
不剥夺:进程已获取到的资源,在使用完毕之前,不能被剥夺,只能在使用完毕后释放;
环路等待:发生死锁时,必然存在一个环路等待链,如P1等待P2的资源,P2等待P3的资源,P3又等待P1的资源,则构成一个环路等待。
为了避免死锁的发生,就要打破产生死锁的4大条件,需要有序地分配系统资源,一个经典的资源分配算法为银行家算法。
4.2.1 银行家算法
例如一家银行有500万的贷款额度,则分配贷款额度有以下原则:
贷款数目不超过500万可以接纳,超过500万则不能接纳;
贷款400万,可分四期发放,每期100万,但每期借200万,超出总的额度则不允许;
如果银行已经借出了200万,但在一个月内可以收回,有需求要在两个月内贷款500万,也可以接纳。
对应操作系统中的资源分配则有:
- 资源满足:当一个进程对资源的最大需求量不超过系统中总资源数时可以接纳该进程;
- 分期请求:进程可以分期请求资源,但请求的总数不能超过最大的需求量;
- 延迟分配:当系统现有的资源不能满足进程尚需的资源数时,对进程的请求可以推迟分配,但需要保证该进程在有限的时间内得到资源。
示例:
- 假设系统中有互斥资源R1、R2、R3,可用资源数量分别为9、8、5,在T0时刻系统中有P1、P2、P3、P4、P5这5个进程,这些进程的最大资源需求量和已分配资源数如表所示,则按____序列执行进程,系统是安全的。
最大需求 R1 R2 R3 | 已分配 R1 R2 R3 | |
P1 | 6 5 2 | 1 2 1 |
P2 | 2 2 1 | 2 1 1 |
P3 | 8 1 1 | 2 1 0 |
P4 | 1 2 1 | 1 2 0 |
P5 | 3 4 4 | 1 1 3 |
思路:
(1).首先计算得到R1、R2、R3剩余的资源数分别为R1=9-7=2,R2=8-7=1,R3=5-5=0;
(2).计算各个进程还需要的资源数量
最大需求 R1 R2 R3 | 已分配 R1 R2 R3 | 仍需分配 R1 R2 R3 | |
P1 | 6 5 2 | 1 2 1 | 5 3 1 |
P2 | 2 2 1 | 2 1 1 | 0 1 0 |
P3 | 8 1 1 | 2 1 0 | 6 0 1 |
P4 | 1 2 1 | 1 2 0 | 0 0 1 |
P5 | 3 4 4 | 1 1 3 | 2 3 1 |
(3). 根据当前系统剩余的资源数(R1=2,R2=1,R3=0)可知,只有P2可以执行,P2执行后释放掉占有的资源,则剩余的资源数R1=2+2=4,R2=1+1=2,R3=0+1=1;接下来P4可以执行,执行后剩余资源数R1=4+1=5,R2=2+2=4,R3=1+0=1;然后P1可以执行,执行后剩余资源R1=5+1=6,R2=4+2=6,R3=1+1=2;接下来P3和P5都可以执行。
(4). 则可执行的序列为 P2→P4→P1→P3→P5 或 P2→P4→P1→P5→P3
5 任务调度(★★★)
5.1概念
任务调度主要解决三个问题:
- 何时分配CPU——任务调度的时机,一共有5种情形;
- 如何分配CPU——任务调度方式,不可抢占和可抢占;
- 分配CPU的原则——任务调度算法,和性能指标;
进程进入系统时,会被添加到作业队列中,这个队列包括系统中的所有的进程。进程在整个生命周期中,会在各种调度队列之间迁移,操作系统通过调度器从这些队列中选择进程。
任务队列:由操作系统维护的一组队列,用来表示系统当中所有任务的当前状态,不同的状态用不同的队列来表示,比如:运行队列、阻塞队列、就绪队列等。
调度器:用来确定多任务环境下任务执行的顺序和在获得CPU资源后能够执行的时间。操作系统通过一个调度程序来实现调度功能:
调度程序本身不是一个任务,是一个函数调用,可在内核的各个部分调用;
调度程序:可以看作CPU的资源管理者,从就绪队列中选择一个任务去运行;调度程序在决策过程中所使用的算法称为调度算法,是在一个特定时刻用来确定将要运行的任务的一组规则。
5.2 调度时机 (★★)
一般来说,下列5种情形,可能会发生任务的调度:
- 任务创建:当一个新的任务被创建时,需要做出一个调度决策,立即执行这个新任务或继续执行父任务;
- 任务完成:当一个任务运行结束时,不再占用CPU,这时调度器需要做出决策,从就绪队列中选择某个任务去运行。
- 任务阻塞:当一个任务由于I/O操作、信号量或其他原因被阻塞时,也必须另选一个任务来执行。
- 阻塞恢复:当一个I/O操作已经完成,等待该I/O操作的任务将从阻塞状态变为就绪状态。
- 中断/时间片用完:中断发生时一般会唤醒一些延时任务,当前任务时间片用完时,也会从运行态变为就绪态。
5.3 调度方式 (★★★)
不可抢占调度方式(Non-Preemptive)
- 一个任务被调度器选中,就会一直执行下去,直到它因为某种原因阻塞,或者主动交出CPU;
- 在这种调度方式下,调度时机中的前3种情况(任务创建、任务运行结束、任务被阻塞),可能会发生任务调度。第4,5种情况(阻塞恢复或中断),不会发生调度。
如图所示,在不可抢占调度方式中,低优先级任务无法被高优先级任务打断。
可抢占调度方式(Preemptive)
- 当一个任务正在运行的时候,调度程序可以打断它,并安排其他的任务去运行;
- 在这种调度方式下,调度时机中的5种情形都可能发生调度。
实时操作系统一般使用可抢占调度方式,确保关键任务的截止时间能够得到满足。
可抢占调度中,低优先级的任务可以被高优先级任务打断。
5.4 调度算法(★★★)
5.4.1 先来先服务 FCFS
先来先服务算法(First Come First Served,FCFS)就是按照任务到达的先后次序进行调度。这是一种不可抢占调度方式,如果一个任务占用着CPU,后续的任务就要等到当前任务运行完毕或阻塞让出CPU后才可以运行;对于一个阻塞的任务,当它被唤醒后,会被放到就绪任务队列的末尾,重新排队。
如图所示,调度器根据任务先后次序进行调度。
优点:简单,易于理解和实现;
缺点:当短任务在长任务之后,将增大平均周转时间(周转时间即任务从提交到完成的时间);
5.4.2 短作业优先算法 SJF
短作业优先算法(Shortest Job First,SJF)的基本思路是:各个任务执行之前,先预计好它的执行时间,从中选择用时较短的任务优先执行。短作业优先算法有两种实现方式:
不可抢占方式:当前任务正在运行时,即使来了一个更短的任务,也要等待当前任务运行结束或被阻塞才会让出CPU,执行新的任务;
可抢占方式:如果一个新的短任务到来,且它的运行时间小于当前正在运行的任务剩余的时间,那么新的任务就抢占CPU去运行。
如图所示,调度器根据任务的执行时间进行调度,优先选择执行时间短的任务。
短作业优先算法可以得到最小的平均周转周期。
任务的周转周期:即任务从到达到执行完毕的时间。
5.4.3 时间片轮转
时间片轮转调度(Round-Robin scheduling,RR)算法,所有的就绪任务按照先来先服务的原则排成一个队列;在每次调度的时候,把处理器分派给队列当中的第一个任务,让它去执行一小段时间(时间片)。任务被阻塞或者完成,或者时间片用完后,它会被送到就绪队列的末尾,然后调度器再执行当前队列中的第一个任务。
时间片轮转调度的优点:
- 公平性:各个就绪的任务平均地分配CPU的使用时间;
- 活动性:每个就绪的任务都能一直保持活动性;
对于时间片轮转算法,需要选择合适的时间片大小,如果时间片太大,每个任务都可以在一个时间片内完成,就失去了轮转的意义,实际上就是先来先服务算法;如果时间片太小,每个任务都需要很多的时间片才能完成,就增加了任务之间的切换时间,增大了系统的管理开销。一般为10ms~50ms。
5.4.4 优先级算法
在嵌入式系统中,不同的任务一般有不同的优先级,优先级调度算法的基本思路是:给每一个任务都设置一个优先级,然后在所有处于就绪状态的任务中选择优先级最高的去运行。
5.4.4.1 调度算法
优先级调度算法分为两种:可抢占式和不可抢占式。一个任务运行时,如果来了一个更高优先级的就绪任务,对于可抢占式调度算法,新任务会立即抢占CPU去运行,而当前的任务就从运行态变成了就绪状态;对于不可抢占式调度算法,新任务需要等待当前任务运行完毕或主动让出CPU后才能运行。
5.4.4.2 优先级确定方式
在任务优先级确定方式上,可以分为静态方式和动态方式两种:
静态优先级方式:在创建任务的时候就确定任务的优先级,并一直保持不变,这种方式一个最大的缺点是:如果高优先级任务一直占有CPU运行,低优先级任务可能长时间得不到CPU,会出现饥饿现象;
动态优先级方式:在创建任务的时候确定任务的优先级,但在运行过程中任务的优先级可以动态改变,以获得更好的调度性能,避免饥饿现象。
5.4.4.3 优先级分组
在优先级算法中,高优先级的任务可以抢占低优先级的任务,为了避免相同优先级的任务冲突,通常的做法是将任务按照不同的优先级分组,在不同组的任务之间使用优先级算法,同一组的任务之间使用时间片轮转法。
5.4.4.4 优先级反转
采用优先级调度算法,还有一个问题是可能会发生优先级反转。即高优先级任务等待低优先级任务占用的资源而阻塞,而低优先级任务又在等待中等优先级的任务。
如图所示:有任务A正在运行,在t1时刻申请了共享资源R;
t2时刻,高优先级的任务B就绪,抢占了CPU,任务A变为就绪态;
t3时刻,任务B需要请求资源R,但此时R被A占用,任务B阻塞;
t4时刻,有一个等优先级的任务C就绪(不使用共享资源R),开始运行;
接下来只要有比任务A优先级高的任务就绪就开始运行,A始终无法运行,也无法释放资源R,就使得高优先级的任务B也无法执行。
5.5 调度算法的性能指标
任务调度算法的主要性能指标有:
CPU的使用率;
响应时间:调度器为一个就绪的任务进行上下文切换所需的时间,以及任务在就绪队列中的等待时间;
周转时间:一个任务从提交到完成所经历的时间;
调度开销:调度器在做出调度决策时所需的时间和空间开销;
公平性:大致相当的两个任务所得到的CPU时间应该大致相同,需要避免饥饿现象;
均衡性:尽可能提高系统各个部分的使用效率;
吞吐量:单位时间完成的任务数量;
6 任务间通信 (★)
6.1概述
任务间通信(intertask communication)指的是任务之间为了协调工作,相互交互数据和控制信息。可以分为两类:
低级通信:只能传递状态和整数值等控制信息,例如信号量。优点是速度快;缺点是传送的信息量少,传递较多信息时需要多次通信;
高级通信:能够传送任意数量的数据,主要包括共享内存、消息传递和管道三类
6.2 共享内存
共享内存(shared memory)指的是各个任务共享他们的地址空间中的某些部分,在此区域,可以任意读写和使用任意的数据结构,将它作为一个通用的缓冲区。
在使用共享内存传送数据时,通常需要加互斥机制,避免出现竞争现象。
6.3 消息传递
消息(message)是内存空间中一段长度可变的缓冲区,其长度和内容均由用户定义。从操作系统来看,所有的消息都是单纯的字节流,没有确切的格式和含义。对消息内容的解释都是由应用完成的,应用根据自定义的消息格式,将消息解释为特定的含义。
消息传递(message passing)指的是任务与任务之间通过发送和接收消息来交换信息。
消息机制由操作系统来维护,包括定义寻址方式、认证协议、消息的数量等。一般提供两个操作:send操作,用来发送一条消息;receive操作,用来接收一条消息。如果两个任务想要通过消息机制来通信,需要先建立一个通信链路,然后通过send和receiver来通信。
任务之间的消息传递可以分为直接通信和间接通信两种:
1. 直接通信
直接通信的方式下,通信双方需要明确与之通信的对象,采用下面的通信原语:
send(P, message): 发送一条消息给任务P;
receive(Q, message):从任务Q那里接收一条消息。
在通信双方之间存在一条通信链路,该链路有如下特征:
- 通信链路是自动建立的,由操作系统维护;
- 每条链路只涉及一对相互通信的任务,每对任务之间仅存在一条链路;
- 通信链路可以是单向或双向的。间接通信
2. 间接通信
在间接通信的方式下,通信的双方不需要指出消息的来源或去向,而是通过共享的邮箱(mailbox)来发送和接收消息,每个邮箱都有一个唯一的标识。这种方式采用下面的通信原语:
- send(A, message):发送一条消息给邮箱A;
- receive(A, message):从邮箱A中接收一条消息。
间接通信的特点:
- 对于一对任务,当它们之间有共享的邮箱时才能进行通信;
- 一个邮箱可以被多个任务访问,每对任务也可以使用多个邮箱来通信;
- 通信可以是单向或双向的。
邮箱只能存放单条消息,它提供了一种低开销的消息传递机制,其状态只有两种:空或满。另外一种间接通信机制是消息队列,可以存放多条消息。
6.4 管道
管道是连接两个任务之间的一个打开的共享文件,专用于任务之间的通信。
如图所示,发送任务从管道的一端写入数据流,接收任务从管道的另一端接收数据流,数据的传输按照先进先出的方式进行。对管道的读写操作是普通的文件读写操作,数据流的长度和格式没有限制。
第二部分 存储管理
1 存储管理-概述
通常在设计存储管理时,需要考虑硬件条件、实时性要求、系统复杂度和性能要求、可靠性等;
为了确保系统的实时性,存储管理需要速度快和确定性。速度快要求存储方面的开销不能太大;确定性则对每一项工作都要有明确的实时约束。
可靠性即是否需要内存保护,内存保护主要包括两个方面:防止地址越界、防止操作越权。在嵌入式微处理器中,存储管理单元(memory Management Unit, MMU)提供了一种内存保护的硬件机制。
2 实模式与保护模式
2.1 实模式方案
实模式也叫内存的平面使用模式,这种模式不使用MMU。主要特点是
- 不划分“系统空间”和“用户空间”;
- 操作系统内核与外围应用程序之间没有物理的边界;
- 系统中的任务全部是内核线程,除了各个线程的上下文和栈,其他资源都是共享的。
实模式优点是:简单、性能好,存储开销确定;缺点是:没有存储保护、安全性差。因此实模式适合规模比较小、简单和实时性要求比较高的系统。
实模式存储管理模式下,嵌入式系统的内存地址一般可以分为五个段:
- .text : 代码段,也称为文本段,包含操作系统和应用程序中的代码;
- .data : 初始化数据段,存放操作系统和应用程序中所有带初始值的全局变量;
- .bss : 未初始化数据段,存放操作系统和应用程序中所有不带初始值的全局变量;
- 堆空间 : 动态分配的空间,比如malloc/free申请和释放的内存;
- 栈空间 : 保存上下文以及函数调用时局部变量和形参;
2.2 保护模式
保护模式方案指的是在处理器中必须要有MMU硬件并启用。主要特点如下:
- 系统内核和用户程序都有各自独立的地址空间:操作系统和MMU共同合作,完成逻辑地址到物理地址的映射;
- 具有存储保护功能:每个应用程序只能访问自己的地址空间,不能去破坏操作系统和其他应用程序的代码和数据。
保护模式的优点是安全性和可靠性比较好,适合规模大、较复杂和实时性要求不太高的系统。
3 分区存储管理
在多道操作系统,同时有多个任务在系统上运行,每个任务都要有各自的地址空间,一般采取分区存储管理:把整个内存划分为两大区域,系统区和用户区,再把用户区划分为若干分区,分区大小可以相同也可以不同,每个任务占用其中一个分区,就可以实现多个任务的并发运行。具体实现上又分为两类:固定分区存储管理和可变分区存储管理。
3.1 固定分区存储管理
各个用户分区的个数、位置和大小一旦确定就不再改变;
优点:易于实现,系统开销比较小;
缺点:内存利用率低、固定的分区数限制了并行任务个数;
3.2 可变分区存储管理
分区不再预先划分好的固定区域,而是动态创建的。装入一个程序时,系统根据程序的需求和内存空间的使用情况来分配。
优点:比较灵活、内存利用率高;
缺点:可能会存在外碎片,内存的分配、回收和管理比较复杂。
可变分区存储管理技术需要考虑三个问题:内存管理的数据结构、内存的分配算法、内存的回收算法。
数据结构上,系统会维护一个分区链表,记录每一个内存分区的情况,包括该分区的状态(是否空闲)、起始地址、长度等信息。
分配算法上,一般有最先匹配法、下次匹配法、最佳匹配法、最坏匹配法。
- 最先匹配法:从头开始,寻找到第一块能否容纳需求大小的内存块来分配;
- 下次匹配法:第一次从头开始寻找到一个合适的内存块,并记录当前位置,下次从当前位置开始寻找;
- 最佳匹配法:寻找一个大小最接近的内存块;
- 最坏匹配法:寻找一个最大的内存块;
在内存的回收中,当一个任务运行结束,并释放它占用的内存,如果该分区相邻位置也是空闲分区,则会将他们合并为一个大的空闲分区。
4 地址映射
地址映射也叫地址重定位,涉及两个基本概念,物理地址和逻辑地址;
物理地址(physical address)也叫内存地址、绝对地址或实地址,对应实际内存上的存储单元,只有通过物理地址才能直接访问到内存单元。物理地址的集合就称为物理地址空间或内存地址空间。
逻辑地址(logical address)也叫相对地址或虚地址。用户的程序编译以后生成的目标代码通常使用的就是相对地址,其首地址是0,其余指令都是相对于这个首地址来编址。
为了保证CPU在执行指令的时候可以正确的访问内存单元,需要将用户程序中的逻辑地址转换为运行时机器直接寻址的物理地址,这个过程就是地址映射。地址映射是由存储管理单元MMU来完成的。
地址映射主要由两种方式:静态地址映射和动态地址映射。
静态地址映射:用户程序装入内存时,直接对指令代码进行修改,一次性实现逻辑地址到物理地址的转换;
动态地址映射:用户程序装入内存时,不对指令代码进行修改,程序运行过程中需要访问内存单元时再来进行地址转换。
5 页式存储管理(★★★)
为了解决分区存储管理的连续性导致的碎片问题,出现了页式存储,它可以打破存储的连续性,使一个程序的逻辑地址空间可以分布在若干离散的内存块上,达到充分利用内存的目的。
页式存储管理的基本思路是:将物理内存划分为固定大小的内存块,称为物理页面;把逻辑地址空间也划分为大小相同的块,称为逻辑页面。页面的大小要求为2^n,一般为512到8K之间。当用户程序装入内存时,不是放在一个连续的区域中,而是用多个空闲的页面把它装进来,这些物理页面也不一定是连续的。
物理地址对应物理页面中的地址,是实际内存中的地址;逻辑地址对应逻辑页面中的地址,是程序中用的地址。地址格式均如下图所示,页号的位数决定了页的多少,页内地址的位数决定了每一页的大小。
如图所示,系统将用户程序分页(逻辑页面),不同的页被放到内存的不同块(物理页面)中,而记录逻辑页面和物理页面的对应关系的就是页表。页表相关的数据是存放在主存中的,所以在进行逻辑地址和物理地址转换的时候,CPU需要访存;页表中的一行信息称为一个页表项,一个页表项对应一个逻辑页号和主存号之间的一对映射关系
从逻辑地址到物理地址的转换如下图所示,页表基地址寄存器存放的是当前任务的页表基地址,将其与逻辑地址中的页号相加,再从页表中找到对应的主存块号(物理页面号),将物理页面号与逻辑地址中页内地址组合就可以得到物理地址。
这种模式下,CPU读写内存需要进行两次访存,一次是访问页表,得到物理页面号;第二次是真正访问数据或指令。这样就降低了内存的存取速度,MMU中的特殊快速查找硬件——快表(Translation Lookaside Buffer,TLB)可以解决这个问题。
页式存储优点:内存利用率高,碎片小(不会超过页面的大小),分配及管理简单;
缺点是:系统需要维护页表,增加了系统的开销;可能会产生抖动现象(页面频繁进出,系统在页面置换上的消耗比实际运行程序的消耗还多)。
根据转换关系,有以下公式:
逻辑页号 = 逻辑地址 / 页面大小;
页内偏移 = 逻辑地址 % 页面大小;
6 段式存储管理
段式存储管理是按照程序自身的逻辑关系划分为若干个段,每个段都有一个段号;内存分配时以段为单位进行分配,每个段在内存中占据连续的空间,各段之间可以不相邻。地址由段号和段内地址组成。
段号位数决定最多可以分多少个段,段内地址的位数决定了段的最大大小。
如图所示,程序分为4个逻辑段(main, X, D, S),在内存中存放在不同的物理段,通过段表记录逻辑段和物理段的对应关系。各个段在内存中段内连续,各段之间不一定是连续的。
从逻辑地址到物理地址的转换如上图所示,根据段号和段表找到对应物理块的基地址,再加上逻辑地址的段内偏移即可得到物理地址。
段式存储的优点是:多道程序共享内存,各段程序的修改互不影响;
缺点是:内存利用率低,内存碎片浪费大。
7 虚拟存储
在操作系统支持下,MMU还提供虚拟存储功能,即使一个任务需要的内存空间超过了系统的内存空间,也可以运行。虚拟存储的实现基于程序的局部性原理。
程序局部性原理,指的是程序在执行过程中的一个较短时间内,它所执行的指令和访问的存储空间都局限在一定的区域内,表现为时间局部性和空间局部性。
- 时间局部性:一条指令的一次执行和下一次执行、一个数据的一次访问和下一次访问,都集中在一个较短的时间内;
- 空间局部性:程序执行某个命令时,它相邻的命令也可能马上被执行;程序访问了某个数据,则它相邻的数据也可能被访问。
程序的局部性原理说明,在一个程序的运行过程中,在某一段时间内,程序只有一小部分处于活跃状态。对于虚拟页式存储模式,只需要将程序的部分页装入内存,程序就可以运行,运行过程中,如果要执行的指令不在内存中,就向系统发出缺页中断请求,系统把对应的页面调入内存,从而使程序继续运行。
虚拟存储具有部分装入和部分置换功能,能从逻辑上对内容容量进行大幅度扩充,实际上为了扩大主存而采用的一种设计技巧;虚拟存储器的容量与主存的大小无关;虚拟存储器的实现对用户来说是透明的。
系统在处理缺页中断时,需要调入新的页面,如果此时内存已满,则需要一定的规则从内存中选择一个页面把它置换出去,也就是页面置换算法。常用的页面置换算法有:最优页面置换算法、最近最久未使用算法、最不常用算法、先进先出算法和时钟页面置换算法。
最优页面置换算法(OPTimal, OPT):发生缺页中断时,对内存中的逻辑页面,计算下一次访问的时间,选择最远的那个置换出去。最优页面算法是一种理想化的算法,实际无法实现。因此通常用作其他算法的性能评价依据。
最近最久未使用算法(Least Recently Used, LRU):发生缺页中断时,从内存中选择一个最近最久未使用的页面置换出去。LRU算法需要记录各个页面在使用时间上的先后顺序,系统开销比较大。具体实现上有链表法和栈方法。
最不常用算法(Least Frequently Used, LFU):发生缺页中断时,置换掉访问次数最少得页面。LFU算法需要设置访问计数器记录每个页面被访问的次数。
先进先出算法(First In First Out,FIFO):选择内存中驻留时间最长的页面,将其置换掉。一般通过一个链表来实现。
时钟页面置换算法(Clock):对FIFO算法的改进,将页面的访问情况也作为淘汰页面的依据。Clock算法需要用到页表项中的访问位,对于内存中的页面,只要被访问,就将其访问位置1;Clock算法基本思路是:将所有的页面组成环形链表(最早的在最前面),发生缺页中断时,从最早的开始查找,如果访问值为0则说明没有被访问过,就淘汰掉;如果访问值为1,说明曾被访问过,暂时不淘汰,并将访问位置0,直到找到一个最早的且没有被访问过的页面。
8 磁盘存储器
8.1 磁盘存储器结构
磁盘存储器有多个盘面构成,每个盘面会分成若干个同心圆的磁道,磁道从外围开始编号,最外圈为0磁道;每个磁道又分为多个扇区,扇区是硬盘的最小存储单元,大小一般为512字节,磁头每次可以读/写一个扇区的数据。所有盘面中相同磁道又构成柱面,为一个立体柱状。
磁盘的容量可以分为非格式化容量和格式化容量,计算公式如下
非格式化容量 = 盘面数 * (每盘面磁道数) * 内圆周长 * 最大位密度
格式化容量 = 盘面数 * (每盘面磁道数) * (每磁道扇区数) * (扇区大小)
磁盘读写数据分为三个过程,首先是将磁头移动到数据所在的磁道,然后盘面将数据所在的扇区旋转到磁头的下方,最后磁头完成数据传输。则响应时间包括三个部分:
寻道时间:磁头移动到数据所在磁道所在的时间;
旋转延迟:盘片将数据所在的扇区移动到磁头下方所需的时间,取决于磁盘转速;
传输时间:完成数据传输所需的时间
8.2 磁盘寻道算法
在磁盘访问中,最主要的时间就是寻道时间,因此要优化磁盘寻道的算法。常用的调度算法有:先来先服务算法、最短寻道时间有限算法、扫描算法、循环算法。
例如,有一个磁盘访问队列,请求顺序为98,183,37,122,14,124,65,67,且当前磁头的位置为53,向着磁道递减的方向移动。
8.2.1 FCFS
先来先服务算法(First Come First Service, FCFS)根据进程请求访问磁盘的先后顺序进行调度。特点是I/O请求的时间会比较长,寻道性能较差。
对于上述场景,寻道顺序是98→183→37→122→14→124→65→67,寻道总时间为(98-53)+(183-98)+(183-37)+(122-37)+(122-14)+(124-14)+(124-65)+(67-65) = 640;
8.2.2 SSTF
最短寻道时间优先算法(Shortest Seek Time First,SSTF)优先处理距离最近的磁道,以便使每一次寻道时间最短。不过该算法不能保证平均寻道时间最短,并且可能最远的磁道一直得不到访问,导致饥饿现象。
对于上述场景寻道顺序为:65→67→37→14→98→122→124→183,寻道总时间为(65-53)+(67-53)+(67-37)+(37-14)+(98-14)+(122-98)+(124-122)+(183-124)=248;
8.2.3 SCAN (电梯调度算法)
扫描算法(SCAN)根据当前磁头的扫描方向向一个方向扫描,并处理途径的请求,扫描到边缘就调转方向去处理剩下的请求。扫描算法不仅考虑磁道距离,也关注磁头当前移动的方向。例如,磁头正自内向外移动,扫描算法所选择下一个访问对象应是既在当前磁道之外,又是距离最近的,这样由内向外访问,直到无更外的磁道需要访问才调转方向,自外向内移动。这种算法磁头移动的规律和电梯运行类似,也称为电梯调度算法。
其特点是:吞吐量大,平均寻道时间小,避免了饥饿现象,不过摆动式的扫描方法,两侧的磁道被访问的频率低于中间磁道。
对于上述场景寻道顺序为:37→14→0→65→67→98→122→124→183,寻道总时间为(53-37)+(37-14)+(14-0)+(65-0)+(98-67)+(122-98)+(124-122)+(183-124)=236;
8.2.4 LOOK
SCAN扫描算法中,磁头扫描到磁盘的边缘才调转方向,可能会带来多余的开销。LOOK算法做了优化,当扫描的方向上没有要访问的磁道时就调转方向,也称为改进的电梯调度算法。
对于上述场景寻道顺序为:37→14→65→67→98→122→124→183,寻道总时间为(53-37)+(37-14)+(65-14)+(98-67)+(122-98)+(124-122)+(183-124)=208;
▶▶示例1
某磁盘的磁头从一个磁道移动到另一个磁道需要10ms。文件在磁道上非连续存放,逻辑上相邻数据块的平均移动距离为10个磁道,每块的旋转延迟时间为100ms,传输时间为2ms,则读取100块数据需要___ms。
思路:
读取一块数据的时间=寻道时间+旋转延迟+传输时间=10*10ms+100ms+2ms=202ms;
则读取100块数据需要202*100 = 20200ms。
▶▶示例2
某磁盘的每个磁道划分为11个物理块,每块存放一个逻辑记录,有R0,R1,R2...R9,R10存放在同一磁道上,记录的顺序如下表所示,如果磁盘的旋转周期为33ms,磁头当前在R0起点处。系统使用单缓冲区顺序处理这些记录,每个记录的处理时间为3ms,则处理11个记录最长需要__ms,如果对信息的存储进行优化,处理时间最少为__ms。
解析:
磁盘旋转一周需要33ms,即从一个物理块旋转到相邻的物理块需要3ms。
1. 首先按照上图左边的方式存放数据。
假设按照 R0,R1,R2...的顺序处理:
R0:当前磁头在R0,读取完R0需要3ms,处理完R0需要3ms,共6ms,此时磁头在R2的起始位置;
R1:如果磁头要读R1,则需要再旋转10个物理块到R1的起始位置,需要10*3ms,再加上读取的处理的时间 10 * 3 + 3 + 3 = 36;此时磁头在R3的起始位置;
R2:如果磁头要读R1,则需要再旋转10个物理块到R2的起始位置,需要10*3ms,再加上读取的处理的时间 10 * 3 + 3 + 3 = 36;此时磁头在R5的起始位置;
以此类推:处理完11个逻辑记录需要 6+36*10 = 366ms;
2. 既然磁头读取加处理的时间磁盘旋转了两个物理块,那么可以按照上图中右边的存放方式:
R0:依旧是读取加处理需要6ms,此时磁头在R1起始位置;
R1:读取加处理共6ms,此时磁头到R2起始位置;
以此类推,处理完11个逻辑记录需要 6*11=66ms。
9 文件管理
9.1 文件系统
文件系统,就是操作系统中用于组织、存储、命名、使用和保护文件的一套管理机制。
文件系统的基本单位是文件。
在嵌入式系统中,文件系统存在于不同类型的存储设备中(如Flash,RAM和硬盘),通常是以中间件或应用程序的方式挂载在存储设备上。常见的文件系统有以下几种
文件系统 | 描述 |
FAT (File Allocation Table) | 最常见的文件系统之一; |
NFS (Network File System) | 网络文件系统,基于远程过程调用和扩展数据表示。 |
FFS (Flashs File System) | 用于flash存储器的文件系统 |
DosFS | 用于实时条件下的块设备访问 |
RawFS | 提供简单的“生”文件系统,将整个磁盘看作一个巨大的文件 |
TapeFS | 用于磁带设备 |
CdromFS | 用于CD-ROM数据访问 |
9.2 文件和目录
文件可以看作一种抽象的机制,提供了一种把信息保存在存储设备上以便访问的方法。文件在创建的时候需要指定一个文件名,由文件名和扩展名组成。
目录(directory)可以看作一个表格,记录了该目录下每个文件的文件名和其他的一些管理信息。目录可以分为绝对路径和相对路径。
绝对路径:从根目录开始表示的路径;
相对路径:从当前目录开始表示的路径。
9.3 文件系统的实现
文件系统的实现主要考虑两个问题,文件的描述方式和文件的存储方式。
文件的描述方法是文件控制块(File Control Block,FCB),它是操作系统为了管理文件而设置的一种数据结构,里面存放了与文件有关的所有管理信息。FCB是文件存在的标志,主要包括两方面的内容:
文件的属性信息:文件的类型、长度、所有者、访问权限、创建和修改时间等;
文件的存储信息:文件在磁盘上的存放位置。
文件的物理结构研究如何把一个文件存放在磁盘等物理介质上。一般有三种形式:连续结构、链表结构和索引结构。
- 连续结构:把文件的各个逻辑块按照顺序存放在若干个连续的物理块中。优点是:简单、易于实现。缺点是:可能产生外碎片,文件的大小不能动态改变;
- 链表结构:把文件的各个逻辑块放在若干个物理块中,各个块之间通过指针连接起来,形成一个链表。优点是:不会有在外碎片,文件的大小可以动态修改;缺点是随机访问比较慢。
- 索引结构:把文件中的每一个逻辑块所对应的物理块编号直接记录在这个文件的文件控制块中,这样的文件控制块称为I节点,或索引节点(index node)。优点是:空间利用率高,查找效率高,扩展性好。
9.4 索引文件
文件系统使用索引来标识文件和目录来快速定位。计算机系统中使用的索引文件结构如下图所示:
系统中有13个索引节点,0~9为直接索引,即索引节点直接指向实际存储文件的物理块,能够表示的逻辑页号范围是0-9。假设每个物理块大小为4K,共可存4KB*10=40KB数据;对于直接索引,直接通过索引节点访问1次对应磁盘即可找到数据。(1次访存)
10号索引节点为一级间接索引节点,即索引节点指向物理块存放的地址项,大小为4KB,假设每个地址占4Byte,可以存放1K个物理块的地址,可表示4KB*1024=4096KB的数据。对于一级间接索引,需要先访问一次索引磁盘,再通过对应的索引访问数据磁盘,从而找到数据位置。(2次访存)
11号索引节点为二级间接索引,即索引节点指向的物理块存放的间接索引的地址项,可以指向1024个存放间接索引的物理块,每个物理块可以存放1024个指向物理块的地址项,能够表示的文件大小为4KB*1024*1024=4GB;对于二级间接索引,需要先访问1次二级地址索引表所在磁盘,再通过对应索引访问1次一级地址索引表,第3次访问数据磁盘找到数据的位置。(3次访存)
9.5 位示图
利用二进制的一位来表示磁盘中的一个盘块的使用情况(一般是0表示空闲,1表示已经分配)。磁盘上的所有盘块都有一个二进制位与之对应,所有盘块对应的位构成一个集合,称为位示图。
10 虚设备和SPOOLING技术
虚设备是一种通过软件技术模拟实体设备的虚拟化技术,可以实现多个不同的虚拟设备共享一个物理设备资源的系统模式。
SPOOLing(Simultaneous Peripheral Operation On Line)外部设备同时联机操作,又称为假脱机输入输出操作或排队转储技术,采用一组程序或进程模拟一台输入输出处理器。
SPOOLING系统必须建立在具有多道程序功能的系统上,而且需要高速随机存储器的支持,通常使用磁盘存储技术,主要由三部分组成:
输入井和输出井,是在磁盘上开辟的两个大存储空间。输入井模拟脱机输入的磁盘设备,用于暂存I/O设备输入的输出;输出井模拟脱机输出时的磁盘,用于暂存用户程序的输出数据。
输入缓冲区和输出缓冲区。为了缓和CPU和磁盘速度不匹配的问题而开辟。
输入进程SPi和输出进程SPo。利用两个进程来模拟脱机I/O时的外围控制机。
常见例子就是共享打印机,打印机实际是一个独占设备。使用SPOOLing技术,可以将其改造为一个共享设备。
特点:
- 提高了I/O的速度;
- 将独占设备改造为共享设备;
- 实现了虚拟设备功能;