一篇文章搞懂Linux内存管理

一、两种内存分配方式

  • 固定分区分配:将内存分成不同大小的分区,按照大小保存在一张分区表中,分区表中包括每个分区的起始地址、大小、以及状态(是否分配),当有程序要装入时,由内存分配程序依据用户程序的大小检索该表,从中找出一个能满足要求的尚未分配的分区,并将分区状态改为已分配。
  • 动态分区分配:利用某种分配算法(下面会依次介绍每种分配算法),从空闲分区表中找到所需大小的分区。比如请求的分区大小为size1,表中每个空闲分区的大小表示为size2,假如size2-size1<=size(size是系统规定的不可再切割的剩余分区大小),则不再切割,否则切割出所需大小,将剩余部分扔留在空闲分区表中;当回收时,假如回收分区前后有空闲分区相邻,则与最前面的空闲分区合并,只需改变最前面分区的大小,否则为回收的分区单独新建一个分区。

二、基于顺序搜索的动态分区分配算法

适合中小型系统,大型系统的内存分区会很多,顺序搜索的分区链比较长,所以搜索可用分区时会很慢。

1、首次适应算法

       该算法优先利用低内存中低地址部分的空闲分区,从而保留了高地址部分的大空闲区。这为以后到达的大作业分配大的内存空间创造了条件。缺点是低地址部分不断被划分,会留下许多难以利用的内存碎片,而每次查找都是从低地址开始,所以越到后期查找可用分区开销越大。

缺点:每次从最小分区开始,查找慢;先划分最小分区,内存碎片多

优点:和下面相比,无

2、循环首次适应算法

      为了解决首次适应算法的2个缺点,循环首次适应算法在为进程分配内存空间时,不再是每次都从链首开始查找,而是从上找到的空闲分区的下一个空闲分区开始查找,直至找到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业。该算法能使内存中的空闲分区分布得更均匀,从而减少了查找空闲分区时的开销。

缺点:由于从中间开始分割,所以会缺乏大的空闲分区

优点:查找快

3、最佳适应算法

      将所有的空闲分区按容量从小到大的顺序形成一个空闲分区链,每次分配内存时,总是把能满足要求的最小空闲分区分配给作业,所以第一次找到的能满足要求的空闲分区必然是最佳的。

缺点:每次分配后所切割下来的剩余部分总是最小的,会造成大量内存碎片

优点:查找快

4、最坏适应算法

      空闲分区从大到小成链,每次分配内存时,总是挑选一个最大的空闲区,从中分割一部分存储空间给作业使用,所以导致存储器中缺乏大的空闲分区,所以成为最坏适应算法。不过优点是可以使剩余的空闲区不至于太小,产生碎片的可能性最小,同时它的查找效率很高。

缺点:缺乏大的空闲分区

优点:碎片少,查找效率高

三、基于索引搜索的动态分区分配算法

1、快速适应算法

      对于每一类具有相同容量的所有空闲分区,单独设立一个空闲分区链表,同时设立一张管理索引表,每个索引项对应一种空闲分区大小,并记录了该类型空闲分区链表投的指针。分配内存时不会切割,会直接分配能容纳所需内存的最小分区。比如内存中分区有2k、4k、8k、16k,当前作业需要7k的内存大小,则分配8k的分区给它,并且不会进行分割

缺点:由于不进行切割,所以会有一定的内存浪费

优点:没有内存碎片、查找效率高

2、伙伴系统算法

      无论已分配区或空闲分区,大小均为2的k次幂,则系统刚开始运行时,整个内存区是一个大小为2的n次方的空闲分区。在系统运行过程中,由于不断划分,将会形成若干个不连续的空闲分区,将这些分区按照分区的大小进行分类,对于具有相同大小的空闲分区,单独设立一个空闲分区双向链表,不同大小的空闲分区形成了多个空闲分区链表。

      当我们要分配大小为n的内存时,会首先计算一个i值,2的i-1次方小于等于n,n小于等于2的i次方,若找到,则把该空闲分区分配给进程,否则在分区大小为2的i+1次方的空闲分区链表中查找,将其分成两个大小为2的i次方的分区,一个分配给进程,剩下的一半放到n的i次方的分区链表中(如果2的i+1次方还没有,则继续依次向上查找)。回收时会执行和分配时相反的过程,同样依次合并。

缺点:分割和合并花费时间较多

优点:使用索引搜索,速度有一定提升;空闲分区合并提高了空闲分区的使用率

3、哈希算法

      在分类搜索和伙伴系统中,都需要在一张管理索引表中查找到所需分区大小对应的链表头指针,如果分区特别细的话,管理索引表会很长,查找开销会变高。哈希则是为管理索引表建立以分区大小为关键字的哈希表,每一项记录对应的空闲分区链表头指针,分配时可以快速得到分区链表。

四、动态可重定位分配

优点:引入逻辑地址,有效降低内存碎片

      在前面介绍的分配算法中,或多或少都会产生很多太小的分区,不能被任何进程利用的内存碎片。所以引入逻辑地址,逻辑地址也叫相对地址,它存储的是物理地址的偏移量。将相对地址转换为物理地址的操作被推迟到程序指令真正执行时,为了使地址的转换不影响程序的执行速度,在系统中增设了重定位寄存器,用来存放程序和数据在内存中的起始地址。在执行时,真正访问的内存地址是相对地址与重定位寄存器中的地址相加形成的。

五、段页式存储管理方式

1、页和块的概念

      页是指的逻辑地址(相对地址),块是指的物理地址。分页存储管理将进程的逻辑地址空间分成若干个页,并为各页从0开始编号,把物理内存地址空间分成若干个块,也从0开始编号。在为进程分配内存时,以块为单位将内存中的若干个页分别装入到多个可以不相邻的物理块中。

2、页表

      在分页系统中,允许将进程的各个页离散地存储在物理内存的任何一个物理块中,为了保证进程仍然能够正确地运行,即能将每个页中的逻辑地址转换个块的物理地址,系统又为每个进程建立了一张页面映像表,简称页表。

      由于页表也是存储在内存中的,这使CPU每存取一个数据时,都要两次访问内存,第一次访问内存中的页表,第二次通过页表中记录的物理块访问真正的物理内存。      

     为了提高地址变换速度,系统中增加了一个特殊的高速缓冲寄存器,取名为TLB(Translation Look aside Buffer),用来存放当前访问的那些页表项(你可以理解成页表的缓存)。所以每次访问时可以从TLB中一次读取出该页对应的物理块号,CPU减少了一次内存访问,这也是为什么进程切换比线程切换开销大的主要原因。

3、段页

      在上面介绍的动态分区方式,都是为整个进程分配一个连续的内存空间,引入分段式存储管理系统后,则为每个分段分配一个连续的分区(代码段、数据段、全局区、堆、栈)。段表中存储了所有的分段长度和对应页的首地址,每个段的页地址是逻辑连续的。

优点:

  • 是进程中的各个段可以离散地装入内存中不同的分区中,不再是必须分配一个连续的内存分区
  • 对于开发人员来说,可以分别编写和编译,可以针对不同类型的段采取不同的保护,比如堆动态申请和释放,栈自动申请和释放
  • 允许若干个进程共享一个或多个分段

 

一枚正在努力的后端程序猿,码字不易,看完了如果对您有帮助,动动小手点个赞评论一下,如果能给个关注再好不过啦!

你可能感兴趣的:(操作系统,进程,线程,内存)