物理内存与线性地址的关系

在硬件工程师和普通用户看来,内存就是插在或固化在主板上的内存条,它们有必定的容量——好比64 MB。但在应用程序员眼中,并不过分关心插在主板上的内存容量,而是他们可使用的内存空间——他们能够开发一个须要占用1 GB内存的程序,并让其在OS平台上运行,哪怕这台运行主机上只有128 MB的物理内存条。而对于OS开发者而言,则是介于两者之间,他们既须要知道物理内存的细节,也须要提供一套机制,为应用程序员提供另外一个内存空间,这个内存空间的大小能够和实际的物理内存大小之间没有任何关系。
   
  咱们将主板上的物理内存条所提供的内存空间定义为物理内存空间;将应用程序员看到的内存空间定义为线性空间。物理内存空间大小在不一样的主机上能够是不同的,随着主板上所插的物理内存条的容量不一样而不一样;但为应用程序员提供的线性空间倒是固定的,不会随物理内存的变化而变化,这样才能保证应用程序的可移植性。尽管物理内存的大小能够影响应用程序运行的性能,而且不少状况下对物理内存的大小有一个最低要求,但这些因素只是为了让一个OS能够正常的运行。
   
  线性空间的大小在32-bit平台上为4 GB的固定大小,对于每一个进程都是这样(一个应用能够是多进程的,在OS眼中,是以进程为单位的)。也就是说线性空间不是进程共享的,而是进程隔离的,每一个进程都有相同大小的4 GB线性空间。一个进程对于某一个内存地址的访问,与其它进程对于同一内存地址的访问毫不冲突。好比,一个进程读取线性空间地址1234ABCDh能够读出整数8,而另一个进程读取线性空间地址1234ABCDh能够读出整数20,这取决于进程自身的逻辑。
   
  在任意一个时刻,在一个CPU上只有一个进程在运行。因此对于此CPU来说,在这一时刻,整个系统只存在一个线性空间,这个线性空间是面向此进程的。当进程发生切换的时候,线性空间也随着切换。因此结论就是每一个进程都有本身的线性空间,只有此进程运行的时候,其线性空间才被运行它的CPU所知。在其它时刻,其线性空间对于CPU来讲,是不可知的。因此尽管每一个进程均可以有4 GB的线性空间,但在CPU眼中,只有一个线性空间的存在。线性空间的变化,随着进程切换而变化。
   
  尽管线性空间的大小和物理内存的大小之间没有任何关系,但使用线性空间的应用程序最终仍是要运行在物理内存中。应用所给出的任何线性地址最终必须被转化为物理地址,才可以真正的访问物理内存。因此,线性内存空间必须被映射到物理内存空间中,这个映射关系须要经过使用硬件体系结构所规定的数据结构来创建。咱们不妨先称其为映射表。一个映射表的内容就是某个线性内存空间和物理内存空间之间的映射关系。OS Kernel一旦告诉某个CPU一个映射表的位置,那么这个CPU须要去访问一个线性空间地址时,就根据这张映射表的内容,将这个线性空间地址转化为物理空间地址,并将此物理地址送到地址线,毕竟地址线只知道物理地址。
   
  因此,咱们很容易得出一个结论,若是咱们给出不一样的映射表,那么CPU将某一线性空间地址转化的物理地址也会不一样。因此咱们为每个进程都创建一张映射表,将每一个进程的线性空间根据本身的须要映射到物理空间上。既然某一时刻在某一CPU上只能有一个应用在运行,那么当任务发生切换的时候,将映射表也更换为响应的映射表就能够实现每一个进程都有本身的线性空间而互不影响。因此,在任意时刻,对于一个CPU来讲,也只须要有一张映射表,以实现当前进程的线性空间到物理空间的转化。
  
  --------------------------------------------------------------------------------
  
  
  
  2. OS Kernel Space & Process Space
  
   
  因为OS Kernel在任意时刻都必须存在于内存中,而进程却能够切换,因此在任意时刻,内存中都存在两部分,OS Kernel和用户进程。而在任意时刻,对于一个CPU来讲只存在一个线性空间,因此这个线性空间必须被分红两部分,一部分供OS Kernel使用,另外一部分供用户进程使用。既然OS Kernel在任什么时候候都占用线性空间中的一部分,那么对于全部进程的线性空间而言,它们为OS Kernel所留出的线性空间能够是彻底相同的,也就是说,它们各自的映射表中,也分为两部分,一部分是进程私有映射部分,对于OS Kernel映射部分的内容则彻底相同。
   
  从这个意义上来讲,咱们能够认为,对于全部的进程而言,它们共享OS Kernel所占用的线性空间部分,而每一个进程又各自有本身私有的线性空间部分。假如,咱们将任意一个4 GB线性空间分割为1 GB的OS Kernel空间部分和3 GB的进程空间部分,那么全部进程的4 GB线性空间中1 GB的OS Kernel空间是共享的,而剩余的3 GB进程空间部分则是各个进程私有的。Linux就是这么作的,而Windows NT则是让OS Kernel和进程各使用2 GB线性空间。
  
  --------------------------------------------------------------------------------
  
  
  
  3. Segment Mapping & Page Mapping
  
   
  全部的线性空间的内容只有被放置到物理内存中才可以被真正的运行和操做。因此,尽管OS Kernel和进程都被放在线性空间中,但它们最终必须被放置到物理内存中。因此OS Kernel和全部的进程都最终共享物理内存。在现阶段,物理内存远没有线性空间那么大——线性空间是4 GB,而物理内存空间每每只有几百兆,甚至更小。另外即便物理内存有4 GB,但因为每一个进程均可以有3 GB线性空间(假如进程私有线性空间是3 GB的话),若是把全部进程的线性空间内容都放在物理内存中,明显是不现实的。因此OS Kernel必须将某些进程暂时用不到的数据或代码放在物理内存以外,将有限的内存提供给当前最须要的进程。另外,因为OS Kernel在任什么时候候都有可能运行,因此OS Kernel最好被永远放在物理内存中。咱们仅仅将进程数据进行换入换出。
   
  从线性空间到物理空间的映射须要映射表,映射表的内容是将某段线性空间映射到相同大小的物理内存空间上。从理论上,咱们可使用两种映射方法:变长映射,和定长映射。变长映射指的是根据不一样的须要,将一个一个变长段映射到物理内存上,其格式能够以下(线性空间段起始地址,物理空间段起始地址,段长度)。假如一个进程有3个段:10M的数据段,5M的代码段,和8K的堆栈段,那么就能够在映射表中创建3项内容,每一项针对一个段。这看起来没有问题。但假如如今咱们的实际的内存只有32M,其中10M被内核占用,留给进程的物理空间只有22M,那么此进程在运行时,就占据了10M+5M+8K的内存空间。随后当进程发生切换时,假如另外一个进程和其有相同的内存要求,那么剩余的22M-(10M+5M+8K)明显就不够用了,这时只能将原进程的某些段换出,而且必须是整段的换出。这就意味着咱们必须至少换出一个10M的数据段,而换出的成本很高,由于咱们必须将这10M的内容拷贝到磁盘上,磁盘I/O是很慢的。
   
  因此,使用变长的段映射的结果就是一个段要么被所有换入,要么被所有换出。但在现实中,一个程序中并不是全部的代码和数据都可以被常常访问,每每被常常访问的只占所有代码数据的一部分,甚至是一小部分。因此更有效的策略是咱们最好只换出那些并不常用的部分,而保留那些常常被使用的部分。而不是整个段的换入换出。这样能够避免大块的慢速磁盘操做。
   
  这就是定长映射策略,咱们将内存空间分割为一个个定长块,每一个定长块被称为一个页。映射表的基本格式为(物理空间页起始地址),因为页是定长的,因此不须要指出它的长度,另外,咱们不须要在映射表中指定线性地址,咱们能够将线性地址做为索引,到映射表中检索出相应的物理地址。当使用页时,其策略为:当换出的时候,咱们只将那些不活跃的,也就是不常用的页换出,而保留那些活跃的页。在换入的时候,只有被请求访问的页才被换入,没有被请求访问的页将永远不会被换入到物理内存。这就是请求页(Demand Page)算法的核心思想。
   
  这就引出一个页大小的问题:首先咱们不可能以字节为单位,这样映射表的大小和线性空间大小相同——假如整个线性空间都被映射的话——咱们不可能将所有线性空间用做存放这个映射表。由此,咱们也能够得知,页越小,则映射表的容量越大。而咱们不能让映射表占用太多的空间。但若是页太大,则面临着和不定长段映射一样的问题,每次换出一个页,都须要大量的磁盘操做。另外,因为为一个进程分配内存的最小单位是页,假如咱们的页大小为4 MB,那么即便一个进程只须要使用4 KB的内存,也不得不占用整个4 MB页,这明显是一种很大的浪费。因此咱们必须在二者之间进行折衷,通常平台所规定的页大小为1 KB到8 KB,IA-32所规定的页大小为4 KB。(IA-32也支持4 MB页,你能够根据你的OS的用途进行选择,通常都是使用4 KB页)。
  
  --------------------------------------------------------------------------------
  
  
  
  4. Page Table
  
   
  假如使用4 KB的页,那么对于4 GB的线性空间,则须要1,048,576个页表实体,每一个表项占用4个字节,则须要4,194,304个字节。仅仅页表就占用4 MB空间,这是一个很大的需求。但若是确确实实一个进程须要使用所有线性空间的话,那么这4 MB的页表空间投入也是必要的。
   
  但在现实中,不多有那个程序须要使用这么大空间,通常的程序每每很小,从几KB到几MB,再使用这么大的页表就纯粹是一种浪费。那咱们该怎么办?
   
  一种策略是创建变长页表——咱们只创建所需长度的页表。但这种策略带来很大的限制,而且仍然会形成比较大的空间浪费。因为页表机制是使用线性地址做为索引,到页表中进行检索。那么若是咱们想让OS Kernel使用C0000000h-FFFFFFFFh,也就是3 G  

相关文章
相关标签/搜索