整理人:zhoumo
一、内存分配路径
上面的这张图片来自国嵌,很好的把内存管理这四个章节的内容概括了出来,
上图显示出了三条内存分配路径:
1、用户空间的内存分配
2、Kmalloc使用slab内存分配
3、vmalloc:线性地址空间连续,物理地址不一定连续
同样的,上图也很好的概括了内存这一块重要的知识点
1、伙伴系统
2、slab分配器
3、进程地址空间
二、分段的由来
三、物理地址扩展(PAE)
问题由来:
为什么我们使用的windows server2008(貌似是这个版本?)是32位的系统,内存却可以支持到64G,不是说只能寻到4G吗?
winserver为什么要使用32位的系统,直接用64位的不行么?
解释:
32位的系统对软件的兼容性好,winserver使用32位,可以兼容更多的软件,那么需要一个折中:要使用32位系统,还要支持更大的内存,那么需要一种新的技术
PAE:
要求使用32位的线性地址,寻到64G的页框,需要对页表项、页全局目录,页目录进行更改,以三级映射为例,线性地址的映射有如下变化:
cr3
指向一个PDPT
位31-30
指向PDPT中的4个项中的一个
位29-21
指向页目录中512项中的一个
位20-12
指向页表中512个项中的一个
位11-0
4KB页中的偏移量
与原来的区别在于,位29-21,位20-12与原来相比,都由32位变成64位,但项数没变,因此“节省出”两位,在30-31加上一级,两位可以映射4个表项,每一项都是64位,cr3负责指向这四个中的一个。
下面来计算一下,因为增加了4个64位的表项,因此,每一项都乘以2,就是16,所以内存可以扩展到4G乘以16等于64G
四、从内存角度思考堆和栈(此部分摘自网络,并非原创)
这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。
因为栈的这种特点,对栈的使用在程序中是非常频繁的。对子程序的调用就是直接利用栈完成的。
和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。基本的malloc/realloc/free 函数维护了一套内部的堆数据结构。
当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间(库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间),如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:
1. 系统调用可能不支持任意大小的内存分配。有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。
2. 系统调用申请内存可能是代价昂贵的。系统调用可能涉及用户态和核心态的转换。
3. 没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。
从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。不同堆分配的内存无法互相操作。
既然没有专门的指令和寄存器,计算机的对堆内存究竟是如何管理的?
所以我们调用free对malloc得到的内存进行释放的时候,并不是直接释放给操作系统,而是还给了glibc的堆管理实体,而glibc会在把实际的物理内存归还给系统的策略上做一些优化,以便优化用户任务的动态内存分配过程。
那么glibc的堆管理器在什么时候才把物理内存归还给系统呢?
由此,我们在使用malloc/free时应该小心,特别是在初始化时分配了好多内存,但是在这之后却再也不需要这么多的内存了,而这块内存又没有达到threshhold值或者在堆的最高线性地址处有某块内存没有释放,但是它前面的所有堆内存都释放了;这种情况下,用户任务将会浪费一些物理内存,这在资源比较紧张的嵌入式系统中是不可容忍的。