searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

【内存管理1】概述

2023-09-26 09:58:54
9
0

一 内存管理概述

在 Intel 平台下,逻辑地址(logical address)是 selector:offset 这种形式,selector 可以是代码段或者数据段,offset 是段内偏移。用 selector 去 全局描述符表 )(GDT)里拿到段基址(segment base address)然后加上段内偏移(offset),这就得到了线性地址(linear address)。这个过程被称作段式内存管理。

如果再把线性地址切成三段,用前两段分别作为索引去PGD(页目录表)、Page Table里查表,会先得到一个页目录表项、然后会得到一个页表项(Page Table Entry),那里面的值就是一个物理内存块的起始地址(其实就是是物理内存编号),把它加上 linear address 切分之后第三段的内容(又叫页内偏移)就得到了最终的物理地址(physical address)。这个过程被称作页式内存管理。

1.内存管理的目标

内存管理的目标需要满足进程之间的隔离、进程与内核之间的隔离、减少物理内存并发使用的数量之外,还有以下几个目标:

  • 减少内存碎片,包括外部碎片和内部碎片。外部碎片是指还在内存分配器中的内存,但是由于比较分散,无法满足用户大块连续内存分配的申请。内部碎片是指申请了5个字节的内存,分配器给你分配了8个字节的内存,其中3个字节的内存是内部碎片。内存管理要尽量同时减少外部碎片和内部碎片。
  • 内存分配接口要灵活多样,同时满足多种不同的内存分配需求。既要满足大块连续内存分配的需求,又能满足小块零碎内存分配的需求。
  • 内存分配效率要高。内存分配要尽量快地完成,比如说设计了一种算法,能完全解决内存碎片问题,但是内存算法实现得特别复杂,每次分配都需要1毫秒的时间,这就不可取了。 4.提高物理内存的利用率。比如及时回收物理内存、对内存进行压缩 

2.原始的内存管理方法

内存是以块形式存在,通过链表将这些块由低地址到高地址的顺序组织起来,在加上内存相应属性信息(大小,性质)就完成了对物理内存的组织。当一块内存被回收时,直接向链表中插入或者合并(当相邻内存块也是空闲时会发生合并成为一个大的内存块),当内存要被使用,那么就从空闲链表中删除,如果链表中物理内存大于程序需求的话,那么该节点中的内存会被切为两块,一块是程序需要的被分配出去,另一块是剩下的留在链表里。 从链表中查找空闲块有以下几种方法:

  • 最优适配原则:遍历一遍链表,寻找一块内存大小和程序需求内存大小最接近的内存块,然后分配给程序。优点:最大效率的利用了内存的空间;对于分配大量小的内存块的情况很有优势,产生的外部碎片更小     缺点:容易产生大量极小的无法被利用的碎片空间;释放内存缓慢
  • 首次适配原则:从链表头开始往后遍历,遇到的第一块大于程序需求内存的结点为目标结点,然后将该结点中的内存分配给程序    优点:实现简单,高地址处会有大量大的空闲块      缺点:外部碎片问题严重;在分配大的内存块时比较慢
  • 最差适配原则:直接将当前最大的内存块分配给程序。   优点:避免了产生大量细小的内存块;当分配的内存块中等大小居多时效果很好     缺点:后续可能就没有大的内存块可供分配了;释放分区缓慢。(按大小组织的分配都有这个缺点,因为需要重新调整链表结构) 

这些原始的分配方法具有一些难以避免的特点:1.分配给一个程序的物理内存是连续的  2.内存利用率低 3.存在外部碎片和内部碎片的问题 

3.Linux内存管理体系

整个体系分为3部分,左边是物理内存,右边是虚拟内存,中间是虚拟内存映射(分页机制)。

先从物理内存说起,内存管理的基础还是物理内存的管理。 物理内存那么大,应该怎么管理呢?

首先要对物理内存进行层级区划,三个层级分别叫做节点(node)、区域(zone)、页面(page)。系统首先把整个物理内存划分为N个节点,每个节点都有一个节点描述符。节点下面再划分区域,每个区域都有区域描述符。区域下面再划分页面,每个页面都有页面描述符。

对物理内存建立三级区域划分之后,就可以在其基础之上建立分配体系了。物理内存的三级分配体系分别是buddy system、slab allocator和kmalloc,分别满足不同规模的内存需求。

物理内存有分配也有释放,但是当分配速度大于释放速度的时候,物理内存就会逐渐变得不够用了。此时我们就要进行内存回收了。

内存回收首先考虑的是内存规整,也就是内存碎片整理,因为有可能不是可用内存不足了,而是内存太分散了,没法分配连续的内存。内存规整之后如果还是分配不到内存的话,就会进行页帧回收。内核的物理内存是不换页的,所以内核只会进行缓存回收。用户空间的物理内存是可以换页的,所以会对用户空间的物理内存进行换页以便回收其物理内存。用户空间的物理内存分为文件页和匿名页。对于文件页,如果其是clean的,可以直接丢弃内容,回收其物理内存,如果其是dirty的,则会先把其内容写回到文件,然后再回收内存。对于匿名页,如果系统配置的有swap区的话,则会把其内容先写入swap区,然后再回收,如果系统没有swap区的话则不会进行回收。把进程占用的但是当前并不在使用的物理内存进行回收,并分配给新的进程来使用的过程就叫做换页。进程被换页的物理内存后面如果再被使用到的话,还会通过缺页异常再换入内存。如果页帧回收之后还没有得到足够的物理内存,内核将会使用最后一招,OOM Killer。OOM Killer会按照一定的规则选择一个进程将其杀死,然后其物理内存就被释放了。

物理内存的这些操作都是在内核里进行的,但是CPU访问内存用的并不是物理内存地址,而是虚拟内存地址。内核需要建立页表把虚拟内存映射到物理内存上,然后CPU就可以通过MMU用虚拟地址来访问物理内存了。虚拟内存地址空间分为两部分,内核空间和用户空间。内核空间只有一个,其页表映射是在内核启动的早期就建立的。用户空间有N个,用户空间是随着进程的创建而建立的,但是其页表映射并不是马上建立,而是在程序的运行过程中通过缺页异常逐步建立的。内核页表建立好了之后就不会再取消了,所以内核是不换页的,用户页表建立之后可能会因为内存回收而取消,所以用户空间是换页的。 

0条评论
0 / 1000
林寒涧肃
3文章数
0粉丝数
林寒涧肃
3 文章 | 0 粉丝
林寒涧肃
3文章数
0粉丝数
林寒涧肃
3 文章 | 0 粉丝
原创

【内存管理1】概述

2023-09-26 09:58:54
9
0

一 内存管理概述

在 Intel 平台下,逻辑地址(logical address)是 selector:offset 这种形式,selector 可以是代码段或者数据段,offset 是段内偏移。用 selector 去 全局描述符表 )(GDT)里拿到段基址(segment base address)然后加上段内偏移(offset),这就得到了线性地址(linear address)。这个过程被称作段式内存管理。

如果再把线性地址切成三段,用前两段分别作为索引去PGD(页目录表)、Page Table里查表,会先得到一个页目录表项、然后会得到一个页表项(Page Table Entry),那里面的值就是一个物理内存块的起始地址(其实就是是物理内存编号),把它加上 linear address 切分之后第三段的内容(又叫页内偏移)就得到了最终的物理地址(physical address)。这个过程被称作页式内存管理。

1.内存管理的目标

内存管理的目标需要满足进程之间的隔离、进程与内核之间的隔离、减少物理内存并发使用的数量之外,还有以下几个目标:

  • 减少内存碎片,包括外部碎片和内部碎片。外部碎片是指还在内存分配器中的内存,但是由于比较分散,无法满足用户大块连续内存分配的申请。内部碎片是指申请了5个字节的内存,分配器给你分配了8个字节的内存,其中3个字节的内存是内部碎片。内存管理要尽量同时减少外部碎片和内部碎片。
  • 内存分配接口要灵活多样,同时满足多种不同的内存分配需求。既要满足大块连续内存分配的需求,又能满足小块零碎内存分配的需求。
  • 内存分配效率要高。内存分配要尽量快地完成,比如说设计了一种算法,能完全解决内存碎片问题,但是内存算法实现得特别复杂,每次分配都需要1毫秒的时间,这就不可取了。 4.提高物理内存的利用率。比如及时回收物理内存、对内存进行压缩 

2.原始的内存管理方法

内存是以块形式存在,通过链表将这些块由低地址到高地址的顺序组织起来,在加上内存相应属性信息(大小,性质)就完成了对物理内存的组织。当一块内存被回收时,直接向链表中插入或者合并(当相邻内存块也是空闲时会发生合并成为一个大的内存块),当内存要被使用,那么就从空闲链表中删除,如果链表中物理内存大于程序需求的话,那么该节点中的内存会被切为两块,一块是程序需要的被分配出去,另一块是剩下的留在链表里。 从链表中查找空闲块有以下几种方法:

  • 最优适配原则:遍历一遍链表,寻找一块内存大小和程序需求内存大小最接近的内存块,然后分配给程序。优点:最大效率的利用了内存的空间;对于分配大量小的内存块的情况很有优势,产生的外部碎片更小     缺点:容易产生大量极小的无法被利用的碎片空间;释放内存缓慢
  • 首次适配原则:从链表头开始往后遍历,遇到的第一块大于程序需求内存的结点为目标结点,然后将该结点中的内存分配给程序    优点:实现简单,高地址处会有大量大的空闲块      缺点:外部碎片问题严重;在分配大的内存块时比较慢
  • 最差适配原则:直接将当前最大的内存块分配给程序。   优点:避免了产生大量细小的内存块;当分配的内存块中等大小居多时效果很好     缺点:后续可能就没有大的内存块可供分配了;释放分区缓慢。(按大小组织的分配都有这个缺点,因为需要重新调整链表结构) 

这些原始的分配方法具有一些难以避免的特点:1.分配给一个程序的物理内存是连续的  2.内存利用率低 3.存在外部碎片和内部碎片的问题 

3.Linux内存管理体系

整个体系分为3部分,左边是物理内存,右边是虚拟内存,中间是虚拟内存映射(分页机制)。

先从物理内存说起,内存管理的基础还是物理内存的管理。 物理内存那么大,应该怎么管理呢?

首先要对物理内存进行层级区划,三个层级分别叫做节点(node)、区域(zone)、页面(page)。系统首先把整个物理内存划分为N个节点,每个节点都有一个节点描述符。节点下面再划分区域,每个区域都有区域描述符。区域下面再划分页面,每个页面都有页面描述符。

对物理内存建立三级区域划分之后,就可以在其基础之上建立分配体系了。物理内存的三级分配体系分别是buddy system、slab allocator和kmalloc,分别满足不同规模的内存需求。

物理内存有分配也有释放,但是当分配速度大于释放速度的时候,物理内存就会逐渐变得不够用了。此时我们就要进行内存回收了。

内存回收首先考虑的是内存规整,也就是内存碎片整理,因为有可能不是可用内存不足了,而是内存太分散了,没法分配连续的内存。内存规整之后如果还是分配不到内存的话,就会进行页帧回收。内核的物理内存是不换页的,所以内核只会进行缓存回收。用户空间的物理内存是可以换页的,所以会对用户空间的物理内存进行换页以便回收其物理内存。用户空间的物理内存分为文件页和匿名页。对于文件页,如果其是clean的,可以直接丢弃内容,回收其物理内存,如果其是dirty的,则会先把其内容写回到文件,然后再回收内存。对于匿名页,如果系统配置的有swap区的话,则会把其内容先写入swap区,然后再回收,如果系统没有swap区的话则不会进行回收。把进程占用的但是当前并不在使用的物理内存进行回收,并分配给新的进程来使用的过程就叫做换页。进程被换页的物理内存后面如果再被使用到的话,还会通过缺页异常再换入内存。如果页帧回收之后还没有得到足够的物理内存,内核将会使用最后一招,OOM Killer。OOM Killer会按照一定的规则选择一个进程将其杀死,然后其物理内存就被释放了。

物理内存的这些操作都是在内核里进行的,但是CPU访问内存用的并不是物理内存地址,而是虚拟内存地址。内核需要建立页表把虚拟内存映射到物理内存上,然后CPU就可以通过MMU用虚拟地址来访问物理内存了。虚拟内存地址空间分为两部分,内核空间和用户空间。内核空间只有一个,其页表映射是在内核启动的早期就建立的。用户空间有N个,用户空间是随着进程的创建而建立的,但是其页表映射并不是马上建立,而是在程序的运行过程中通过缺页异常逐步建立的。内核页表建立好了之后就不会再取消了,所以内核是不换页的,用户页表建立之后可能会因为内存回收而取消,所以用户空间是换页的。 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0