一个进程在计算机上运行,操作系统必须为其分配存储空间,使其部分或全部驻留在内存,因为CPU仅从内存中读取程序指令并执行,不论直接从外存上读取程序。不能何种操作系统的存储管理能够实现:内存分配、地址变换、存储保护、存储共享和存储扩充。
内存分配
内存分配是为每道程序分配内存空间。分配的方式主要有三种:
(1)直接指定方式:程序员在编写程序的时候,或编译器在编译程序的时候采用的是实际内存的地址。采用这种方式的前提是已知存储器的可用容量。
(2)静态分配方式:用户或编译器编译的目标程序时都从0开始的编址方式。当把他们装入内存时,给他们分配存储空间,并确定他们在主存的实际位置。这种方式在一个作业装入内存时就必须分配其所要求的所有内存空间,如果没有足够的内存空间,则不能装入该进程。一旦进程装入内存,一直占用着分配给他的内存,在其运行过程中不能申请额外的内存空间。
(3)动态分配方式:作业在内存中的位置也是在装入是确定,但是在进程执行过程中可以根据需要申请附加的内存空间。一个作业已占用的部分内存空间不需要可以归还给系统,这种方式能实习个别存储区域的动态分配和回收。
注:内存管理还包括内存空间的回收
地址变换
地址变换是指逻辑地址到物理地址的变换,为此必须清楚这些概念:
(1)地址空间:编译程序在对一个源程序时,总是从0号单元开始为其分配地址的,其他所有地址都是从这个地址开始地址顺序排下来的,所以地址空间中的所有地址都是相对于起始地址的,因此逻辑地址也叫相对地址。(虚的概念)
(2)存储空间:一个数据在主存中的位置成为物理地址或绝对地址(实的概念)。一个编译好的程序存在于它自己的地址空间采用的逻辑地址空间,当装入内存运行时,转换成物理地址。
(3)地址重定位:由于作业装入而进行的地址变换叫做地址重定位。
任何实际程序都是由单独的模块组成,然后这些模块链接并加载到内存,这样程序才能运行。当编译器或汇编器编译一个单独的模块时,它不知道这个模块会被加载内存的何处,因此编译每个模块都是从地址0开始的。当这个模块真正装入内存时,它一般不可能是从0开始的,这样就需要一个地址转换,称之为重定位。重定位又可以分为静态重定位和动态重定位。
(1)静态重定位:在程序被加载到内存之前已经知道了它将要加载到内存的起始地址,这样就可以事先进行地址转换,把相对地址转换成绝对地址。采用静态重定位方式,当一个程序加载到内存后,其地址固定不变。(对应于前面的静态分配方式)
(2)动态重定位:作业装入内存后所有的地址仍是相对地址,将相对地址转换成绝对地址的过程被推迟到程序指令要真正执行时进行。动态重定位需要硬件---重定位寄存器的支持。在重定位寄存器存放程序内存的起始地址。相对地址与重定位寄存器的值相加就是程序要访问的内存地址。这样,当加载的模块要在内存移动,就只需要修改重定位寄存器的值就可以了,不需要对程序做任何改变。
存储保护
每个进程都应受到保护,以免被其他程序有意或无意的干扰。因此一个进程以外的其他进程中的程序未经授权不能访问该进程的内存单元。
(1)地址保护:通常用户不能访问操作系统的部分,一个进程通常也不能访问其他进程的地址上去。
(2)权限保护:不同的进程有不同的存取权限,如果对一个只具有执行权限的进程进行写操作,这是违法的。内存保护的需求必须由处理器来满足而不是操作系统。
存储共享
保护机制具有一定的灵活性,允许多个进程访问主存中的同一部分。例如许多进程在执行同一个程序,则允许进程访问该程序的同一个副本一比让每个进程拥有单独的副本效率更高。内存管理系统必须允许对内存共享区域受控访问。
存储扩充
通过某种手段实现内存空间的逻辑扩充。使用户感觉好像有一个更大的内存,并不是实际扩大内存。满足如下功能:
(1)请求调入功能:利用程序的局部性原理,把程序的一部分装入内存,使其先运行,在运行过程中如果需要访问的数据没有装入内存,可向操作系统提出申请,由操作系统从磁盘上把进程所需要的部分调入内存。
(2)置换功能:若发现内存中已经没有空间装入需要的数据,系统应该把能将内存中暂时不用的程序和数据调出内存,存到磁盘上,以释放空间,来装入所需的进程部分。
虚拟内存技术的雏形:交换和覆盖技术
(1)交换技术:当内存中某些进程处于阻塞状态,但他们却占有大量的内存,甚至所有的进程都处于阻塞状态而使CPU被迫停止等待时,这时有许多作业在外存等待进入内存,造成资源浪费。引入“交换”的概念,把内存中暂时不用的程序和数据换出到外存上去,以释放内存空间,当换入到外存的数据再次使用时,把他们换入内存。为了保存内存换出的数据,操作系统会在外存上设置一个交换区。
(2)覆盖技术:覆盖技术是指把程序划分成若干个功能相对独立的程序段,按照自身的逻辑结构将那些不会同时执行的程序共享同一块内存区域。
1.进程控制块(PCB)
进程由PCB数据结构、程序和数据(结构)等组成。
进程控制块包含进程的描述信息、控制信息以及资源信息。进程在创建时应该首先创建其PCB,当进程完成后,系统则释放PCB,进程随之消亡。
进程控制块包含如下几个部分:标识符信息、处理机状态信息、进程调度信息和进程控制信息。具体如下:
(1)进程标识符:每个进程都有唯一的标示符,以区别系统内部的进程。
(2)进程当前状态:说明进程的当前状态,以作为进程调度程序分配处理器的依据。
(3)进程队列指针:用于记录PCB队列下一个PCB的地址。系统中PCB可能组织成多个队列,如就绪队列、阻塞队列等。
(4)程序开始地址:进程执行从此地址开始。
(5)进程优先级:反应进程的紧迫程度。
(6)CPU现场保护区:当进程因某种原因释放处理器时,CPU现场信息被保存在PCB的该区域中,以便进程重新获得处理机后继续执行。
(7)通信信息:记录进程在执行过程中与别的进程所发生的信息交换。
(8)家族联系:
(9)占有资源清单:列出进程所需资源及当前已分配的资源。
通过对PCB的操作,系统为有关进程分配资源从而使有关进程得以被调度自行,执行结束后,通过释放PCB来释放进程所占用的资源。
2.进程状态及状态转换
尽管每个进程是个独立的实体,具有自己的程序计数器、运行空间等。但由于程序或进程之间可能由于等等I/O操作、竞争资源以及相互协作等原因产生了走走停停的动态性。因此,一个进程在生存周期内,可有多种状态。进程在生存周期内至少有三种基本状态:
(1)运行状态:当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。(涉及调度的时机,例如I/O完成产生一个I/O完成中断信号后引起调度)
(2)就绪状态:当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。根据PCB的形式将其排成一个或多个就绪队列。(由于进程分配的时间片用完产生定时中断,而将进程从运行状态转移到阻塞状态,同时调度进程)
(3)阻塞状态:阻塞状态也称等待/封锁/睡眠状态(等待的事件发生唤醒它就ok了)。正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。同样根据PCB的形式嘴直一个或多个阻塞队列。(进程遇到I/O指令,利用访管指令或者系统调用,自愿性中断进入中断处理,最终可能是由于没有所要求的资源而进入阻塞状态)
一个操作系统设计多少状态与系统对进程的管理方式有关,也与系统的资源有关。
进程在生存周期内可以由一个状态转换到另一个状态,具体如下:
(1)就绪→执行:选择一个就绪队列的进程,分派处理机给它,该进程便由就绪状态转变成执行状态。(具体怎么选择进程由调度策略决定)
(2)运行→就绪:处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出处理机,于是进程从执行状态转变成就绪状态。
(3)运行→阻塞:正在执行的进程因等待某种事件发生而无法继续执行时,或因为某些条件无法得到满足,便从执行状态变成阻塞状态。
(4)阻塞→就绪:处于阻塞状态的进程,若其等待的事件已经发生,于是进程由阻塞状态转变为就绪状态。
3.挂起状态的引入
由于处理器的速率要远远高于I/O的速率,致使所有进程都已计算完,都在等待I/O而处于阻塞状态,这时就绪队列中没有运行的作业,从而CPU空闲下来。但在后备作业上(外存)希望“计算”的进程由于系统此时无法容纳新的作业而无法进入内存运行。解决这个问题的办法有两种:
(1)扩内存:不太现实。
(2)软件方法:将一些处于阻塞状态的进程将其部分或全部转移到交换区(磁盘辅助空间),这种情况下引入暂时挂起状态——使一些进程已占用的系统资源让出部分或全部,以供其他进程利用。
这种情况下,进程状态转移如下图:
(1)活动阻塞→静止阻塞:当前系统中没有就绪态进程,就将处于阻塞态的进程至少挂起一个,从而进入静止阻塞状态,为没有被阻塞的进程让出主存空间。
(2)静止阻塞→静止就绪:等待事件发生了,则将静止阻塞状态改为静止就绪。
(3)静止就绪→活动就绪:主存中没有就绪进程,操作系统需要调入一个进程。还有一种情况静止就绪的进程优先级高于就绪状态的优先级时,操作系统往往将处于静止就绪的进程激活为就绪状态。
(4)活动就绪→静止就绪:通常操作系统更倾向于将活动阻塞状态挂起为静止阻塞。因为就绪态进程属于可运行状态,而阻塞态进程占据着主存空间又处于暂时“无法运行”状态。有一种情况是处于高优先级的阻塞进程能很快进入就绪状态,则可能会挂起低优先级的就绪进程而不是高优先级的阻塞进程。
(5)静止阻塞→活动阻塞:比较少见。因为一个进程处于阻塞状态,且不在内存中,调它如内存意义不大。有种情况时静止阻塞中存在一个进程的优先级比静止就绪队列任何一个进程优先级都高,且操作系统确定等待的事很快会发生。
(6)运行→静止就绪:如果在静止阻塞队列中有优先级更高而不会再被阻塞,则操作系统会立刻抢占这个进程,将当前运行的进程转到静止就绪。
引起挂起的主要原因式提供更多的主存空间,以调入可运行的进程或为其他进程分配更多的空间。
注:挂起与激活一对,阻塞与唤醒
4.进程的阻塞与唤醒
引起进程阻塞的原因通常:
(1)启动I/O操作:当进程运行过程中发现需要I/O操作,则进程启动I/O之后,便自动进入阻塞状态进入阻塞队列,等待I/O的完成。
(2)请求系统资源:进程运行过程中向系统申请资源,由于某种原因,系统暂时无法满足进程的要求,就将该进场转换为阻塞状态。
(3)同步约束:对于一些合作或协作的进程之间由于推进的速度存在着差距,而使得有的进程必须等待而进入阻塞。
(4)服务进程无服务:操作系统的一些系统服务进程在完成服务之后,将自己阻塞起来,加入阻塞队列等待新的任务到来。
阻塞的过程如下:
(1)停止当前进程的运行,保护CPU现场到该进程的PCB现场保护区。
(2)将进程执行状态修改为阻塞状态,插入到阻塞队列。
(3)转为进程调度程序重新选择一个就绪队列的进程投入运行。
进程唤醒:
进程的唤醒由另一个相关的进程(系统进程或者由事件发生进程)通过系统调用唤醒原语将其唤醒,进程不能自己唤醒自己。
唤醒过程:
(1)将相关的阻塞进程从阻塞队列摘出。
(2)修改进程的状态为就绪状态,插入到就绪队列。
(3)调度程序进行新的调度,继续恢复原当前进程。
5.进程的挂起与激活
进程挂起过程:
(1)找到需要挂起进程的PCB,将其从相应队列摘出。
(2)将其空间归还系统(由系统存储管理模块负责回收)。
(3)判断被挂起进程的状态,如为阻塞状态转为静止阻塞,如为就绪转为静止就绪。
(4)申请交换区(外存)空间,将部分或全部映像写的交换区,将交换区地址计入PCB中。(PCB依旧在哈)
(5)如被挂起进程为当前进程,则要执行调度程序。
进程激活:
当出现激活事件时,若进程映像驻留在交换区且内存空间满足时,系统利用激活原语将指定的进程激活