文章目录
- 0. 概述
- 1. 为什么需要非连续内存分配
- 2.分段
- 3. 分段寻址方案
- 4. 硬件方案
0. 概述
- 首先要考虑的一个问题就是为什么要用非连续内存来管理现在物理内存。
- 当前具有的非连续物理内存的管理方法,主要涉及到的分段和分页这两种方式。
- 以及关于分页中的一种很重要的数据结构页表 paste tabel,它的涉及组成。
1. 为什么需要非连续内存分配
-
首先为什么要选择这种非连续的内存分配管理方式?
采取连续内存分配管理方式,无论是首次适配、最佳适配、最坏适配方法其实都潜在会带来一系列的问题,比如说会带来内碎片或者外碎片,那么能不能有一种有效的办法来避免这种内碎片和外碎片?如果采取连续内存分配方式,这几种方法其实都会存在或多或少的碎片问题,那其实就考虑了能不能通过一些新的手段,比如说非连续内存分配方法来改善这种情况,这才是重点要考虑问题,如果采取了非连续的内存分配方法,就可以解决这种外碎片问题或内碎片问题,同时也可以让运行的程序能有效地去做隔离和内存资源管理,还可以支持共享数据和共享代码。这样就好比在程序运行中碰到的常见的共享数据,或者共享的库,这都是非连续内存管理可以有效地实现。
-
既然非连续内存管理有这么多好处,那它有没有潜在的问题?
其实非连续内存管理最大的问题在于管理开销本身,可以说通过软件或者硬件的方式来完成虚拟内存地址,或者叫逻辑内存地址到物理内存地址的转换,那转换过程可以用软件方法来实现,也可以用硬件方法来实现。总的来说,如果用软件方法来实现,它带来的开销是相当大的。 -
所以说需要考虑能否和计算机系统的硬件结构结合,特别是 CPU 里面关于内存管理的硬件组成部分结合在一起,共同协同在一起来完成硬件支持非连续内存管理分配,这实际上是重点考虑的问题
主要有两种方法,第一种方法是分段机制,第二个是分页机制。看看结合硬件管理之后,怎么能够有效地实现非连续内存管理。
2.分段
首先看看分段管理机制,考虑两点:
- 第一点在分段情况下内存地址空间怎么寻址?
- 第二个是怎么去实现这种分段寻址机制?
- 在分段机制之下,应用程序看起来什么样子?
看上图可以看到,计算机程序也是由各种各样的段来组成的。比如说在代码执行方面,看到有主程序,有各种各样子程序,还有一些共享库,这些形成了代码的不同分段。那另一方面数据也是一样,有栈段,有堆段,还有共享数据段,那其实不同段之间是有不同属性的。如果说有一种方法能够把这些段有效地给区别出来,隔离出来的话,那么其实有助于更好地管理程序,其实分段机制就想根据应用程序执行的这个特点来对它进行有效的分离和管理。
从上图可以看出来,从应用程序编写来说,或从应用程序运行来说,它虚拟的逻辑地址空间是连续的空间,但是虽然说它是连续空间,通过分段之后可以把它有效地隔离开来,上图可以看到可以把这个程序的堆栈段放在某特定的地址,用特定的管理权限管理起来,把它运行的栈、数据、库,程序的代码段、以及特殊的其他代码段都可以给相应地分离出来。
那分离出来有什么好处呢?
比如说可以让用户代码段和主程序段能够共享,相互之间访问。可以让有些数据和另外一些数据相互隔离,有些数据是可以读写的,另外一些数据是只读的,而且它们位于不同的区域,这样可以更有效地对它进行管理分配,同时也可以保证保护机制的实现。
上图可以看到,左边是连续的虚拟地址,右边是一些不连续的物理地址,那么中间就需要有一种映射机制来把相互之间给建立好对应关联。
从另一个角度来看,可以把左边运行的执行程序的逻辑地址空间,看成一维的线性数组,一个大的连续字节流,通过段机制的映射,可以把不同的内存块,比如代码、数据、堆、栈分别映射到不同的内存中的段中去。可以看到这里面1234是连续的,映射到物理内存中之后,它们大小不一样,位置也不一样,中间的关系,就是通过段机制来实现的。
前面提到这种机制可以用软件来实现,但是如果用软件来实现,对于每一次的内存访问,要通过软件来进行映射,那么这个开销是很大的,所以需要能不能通过硬件机制来支持?
3. 分段寻址方案
需要了解怎么通过硬件机制来支持分段寻址,那首先要了解地址怎么来表示。
前面讲到如果编写应用程序,或者让应用程序跑起来,那么它的地址其实还挺简单,它是一维的逻辑地址。
那一维逻辑地址怎么能够和分段的物理地址能对应呢?
那这里面其实有一个表示的方法,很明显刚才已经说到,一维的逻辑地址其实是由不同的段组成的,这个段可以不连续。那么首先能不能把一维地址分成两块,一块是段寻址,另一个是段内偏移寻址,就可以看到上图中所示,它有一个segment number 段号,另外一个是段内的地址,address。
这两部分合在一起就形成了以段机制来管理的寻址方式。当然这种方式区分出两种情况,如果说段号是和 offset 地址是分开的,那么这是一种段寄存器加地址寄存器寻址方式。比如说 X86 就是典型这种方式。那么另一类可能是说把段和段内的偏移合在一起,形成完整的地址,没有专门的段寄存器的概念来把段的编号单独管理起来,那这种方式是单一地址管理方式,这样两种方式都可以存在。
4. 硬件方案
现在可以看看,如果说有硬件支持,怎么能够把这种段映射机制给建立起来?
也就是说从一维线性的逻辑地址空间能够通过这种段机制映射到不同的物理地址空间去,而这个物理地址空间是由不同的段组成的,通过上幅图做一个讲解。
从左边开始看起来,左上角是一个可以运行的应用程序,那么通过 CPU 来执行每条指令,那么 CPU 就要去寻址,比如说要找到数据在什么地方?代码在什么地方?可以采取单一的段地址管理机制,把一个逻辑地址分成两块,它的上半部分可以理解为是段号,下半部分是段内的偏移。
首先看段号,通过段号希望能找到这个段所在物理空间的起始地址,这是一个需要去了解的,那么这个信息怎么来保存?就需要有一个硬件机制,称之为段表 segment table,段表里面就正好存在了映射关系,就是逻辑地址的段号和物理地址的段号的对应关系。再者,每个段它的大小是不一样的,需要去了解这个段的起始地址是多少?整个长度的限制是多少?那么这个信息也是放在段表里的,也意味着在段表里面有两个很重要的信息,第一个信息是段的起始地址,第二个信息是段的长度限制。
段表里面 index 称之为断表的索引,每一项段表的索引就是由段号来决定,段号就决定了在段表中的位置,index 实际就是 segment number, 这两者是完全一样的。通过 segment number 作为 segment table 的index,就可以找到对应的段项。那么找到这个项之后,自然就知道了这一项所代表这个段,它在物理内存中的起始地址,以及限制在什么地方,那么查出这个信息之后,CPU 会去做一个比对,怎么比对呢?
看一看表示段地址是否满足这个限制,如果说满足这个限制,意味它是合法的寻址。如果不满足限制,那意味着访问这个段超出这个段的范围,非法的访问,那会根据前面讲的异常机制,可以知道这时候 CPU 会产异常,交给操作系统去处理,它来决定程序是不是访问有问题,有问题的话就会把它杀死,这是一种方式。
但一般情况下地址访问是在合理区间之内,那么就可以说,这次访问是合法的,就会继续往下走。把它的起始地址加上它前面 offset,加在一起形成一个物理地址,根据这幅图的右边可以看到,当然形成物理地址之后,就根据地址来查找它在物理内存中的位置,把相应的数据给取出来,交 CPU 做进一步处理。这是整个基于硬件的分段管理机制。
CPU 为了完成寻址做了很多工作,那有一个问题,在这里面这个段表谁来建立?
段表其实是由操作系统来建立,就是在正式寻址之前,就应该把建立好,就是操作系统来建立段表,当操作系统建立好段表之后,这时候段机制就可以正常工作了。至于怎么去具体地建立段表和具体硬件是有很紧密的联系。