Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介

一、x86 Linux 32位系统虚拟地址空间布局:

这里的段基址从0开始,可以访问管理的内存是4G;如果是段基址是从2G开始的,那么内存可以达到6G.
Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第1张图片
Linux进程使用内存的几种类型:

  1. 初始化数据段、未初始化数据段;
  2. 栈(局部变量、临时变量)esp、rsp;
  3. 堆(动态申请)紧贴数据段;
  4. 内存映射段。

其中:

  • 初始化数据段、未初始化数据段、栈的内存使用由编译器和连接器控制;
  • 堆和内存映射段的内存使用由程序编码控制;
  • 数据段是初始化数据段、未初始化数据段和堆的统称,堆内存也是被归到数据段的;
  • 内核空间映射到虚拟地址空间是为了用户使用系统调用而存在的,如果不把内核空间映射进来,用户触发系统调用将找不到内核对应的代码。所以干脆直接映射到地址空间,不用每次都加载内核代码,影响效率。
系统调用一般通过触发int 80中断陷入内核,一些比较新的芯片通过syscall指令来陷入内核。

Memory Mapping Segment:
内存映射段,用来映射文件,对应mmap()系统调用。

二、动态内存管理基础:

在Bss和Heap之间有一段Random brk内存,保证堆内存的起始地址是随机的,为了程序的安全,防止被攻击,因为数据和指令内存一般容易计算,如果紧接着就是堆的话,就很不安全。

动态的申请内存

  1. brk申请内存的方式
    1.1 扩大数据段的范围,向高地址方向扩展;
    1.2 是线性的扩展(申请由低向高,释放是从高向低回收);
由于brk()扩展内存是线性的,他是由指针向上偏移获取内存,释放时需要向下挪动,
但是,如果要释放的内存上面的内存还没有使用完毕,就不能挪动释放

当我们用户申请内存<128kB 时,默认使用的是brk,当申请的内存大于128时,默认将使用mmap(), 由常量M_MMAP_THRESHOLD=128k 规定限制.

  1. mmap申请内存的方式
    2.1 建立内存映射区来获取内存(直接到mmap区取一块内存来使用);
    2.2 非线性扩展(自由扩展,)
由于是直接从mmaping区域获取一整块内存,不是线性的,释放的时候比较容易
直接释放,与线性挪动brk指针释放而区别。

注意:
我们平时开发使用的malloc和new,一个是glibc的库函数,一个运算符重载,而malloc使用brk和mmap来获取内存;
我们通过malloc或者new申请内存时,申请的内存全是虚拟内存,直到使用的时候才会真正个分配物理内存,所以我们在程序调试时打印的地址也是虚拟地址,虚拟地址空间上的地址。

三、brk()和sbrk():

  1. 通过修改program break的值改变数据段的大小(一般紧跟在bss段后面);
  2. brk直接修改program break的地址,sbrk相对移动program break指针;
  3. 限制:RLIMIT_DATA,brk&sbrk无论怎么动也不能把数据段扩展到这个值以上(通常与page对齐);
  4. brk是系统调用,sbrk不是。(编程中使用的brk&sbrk都是对brk系统调用的封装

glibc库函数:
int brk(void *addr); addr=修改为program break地址; return=0、-1
void *sbrk(intptr_t increment); increment=program break增长的长度;return=旧program break的地址、失败返回-1。

sbrk()成功为什么返回旧地址:旧地址 + 申请内存的长度

库函数brk()和sbrk()调用的全是系统调用中的brk()。

系统调用brk:return=新program break地址、旧program break地址。

在Linux上,sbrk()被实现为使用brk()系统调用的库函数,并执行一些内部簿记,
也就是说sbrk()内部实现的时候会记录旧的地址,以便它可以返回旧的中断值。

注意:我们在编程中使用的绝大多数函数如fork()、brk()等都是库函数,不是系统调用,只不过我们使用的这些函数刚好和系统调用的名字相同。

四、mmap()管理内存:

  1. 映射文件(设备)到内存(语义);
  2. MAP_ANONYMOUS参数不映射任何文件(用于内存分配),fd参数忽略(兼容性约定为-1),offset为0;
  3. 用于大内存申请;
大内存不适用brk(),是因为brk是线性挪动的,假设一开始申请一大块内存,
后面由申请一些小内存,如果不释放小内存,大内存就没法释放,这样系统资源压力会很大
  1. 匿名映射取得内存空间,当访问时触发硬件上的异常:缺页中断,获得物理内存。
发生缺页中断,cpu获取中断信息,去执行对应的中断处理程序,分配物理内存
如果内存不够,将一些进程信息移动到swap分区,然后再分配物理内存。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

  • addr = 映射到进程空间的地址hint,为nullptr时操作系统选择地址(不兼容做法);
建议地址,最终不一定再这里开辟,最终由内核决定,只是建议使用这个地址,或者相邻地址
如果是空指针,完全由内核决定
  • length = 映射内存空间长度(申请内存大小);
  • prot = 内存的权限;
r w x  等权限,权限是针对页说的,可读还是可写说的是这个页可读还是可写,这些
信息是记录在 页表 中的
  • flags = 共享方式;
  • fd = 被映射文件描述符;
  • offset = 被映射文件偏移;
  • return=映射内存地址、-1。

五、ptmalloc简介:

glibc的内存管理就是ptmalloc,ptmalloc调用的brk()、mmap()系统调用。

glibc自建内存池对已获得系统内存进行管理(chunk):

  1. 双向链表连接可用chunk进行内存分配。【优化点】
需要双向查,数据的组织是有规律的,因为能判断出,查的时候是向前还是向后查

优化:
建树查询,这个树由一定的平衡性。时间复杂度比链表查询更快
  1. 空闲链表管理(bin,箱子);
  2. 空闲链表分为fast、unorderd、small、large四类chunk:
    3.1 fast - 空间换时间,高速分配和释放;
    3.2 unorderd - 缓存,暂时先不排序;
    3.3 small - 大小小于512bytes;(排序)
    3.4 large - 大小大于512bytes.(排序)
  3. 堆边界管理:top chunk(brk调用扩展的堆内存);
蓝色部分是进程已经存在的,而需要brk管理的是白色区域的堆内存

Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第2张图片
5. 内存映射区管理:mmaped chunk(mmap调用获取的内存)。

下图为ptmalloc的内存管理组织方式:
Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第3张图片
fast:并不存在与上述空间,因为fast是空间换时间的概念。

六、三种内存管理方案的区别:

  1. 多线程模式:
    1.1 ptmalloc - 分区内存池、分区加锁;
    1.2 tcmalloc - 线程内存池(二次分配),主内存池加锁,线程有自己的独有的内存池;
    1.3 jemalloc - 线程内存池、分区内存池、分区加锁。
  2. 空闲内存块管理方式
    2.1 ptmalloc - 多级缓存(快速空闲列表、无序空闲列表、大/小空闲列表)
    2.2 tcmalloc - 多级缓存(小内存走线程缓存内存管理、大内存走主内存管理)
    2.3 jemalloc - 多级缓存(小内存走线程缓存内存管理、大内存走区内存管理、巨内存走全局内存管理)
  3. 总结:
    3.1 ptmalloc和tcmalloc使用链表管理空闲内存。
    3.2 jemalloc用红黑树实现对数复杂度查找空闲内存,使用bitmap实现常数级局部查找。
    3.3 tcmalloc和jemalloc对队列进行分类(Linux伙伴系统)
    3.4 tcmalloc和jemalloc有线程缓存,ptmalloc是征用线程缓存

七、Linux伙伴系统:

  1. 伙伴系统是Linux管理物理内存的一种算法;
  2. 以页为单位管理物理内存;
  3. 幂次方分配器(多level):leveldb的level,tcmalloc的多组分配器。
    Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第4张图片

八、计算机内存分配的发展历史:

  1. 最初:
    Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第5张图片
  2. 引入虚拟地址,MMU,单个的地址映射:
    Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第6张图片
  3. 页管理,走以页为单位的映射:
    Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第7张图片
  4. 由于页本身也比较费空间,所以引入页目录管理页:
    Linux内存管理基础---pt、je、tc三种不同malloc版本简介、内存优化思路、伙伴系统简介_第8张图片

九、内存管理的优化思路:

通用内存管理系统的缺点

1.过于通用设计,考虑平均情况,中庸之道;

2.特殊内存使用方式无优化;

3.对系统资源使用过于保守。

特殊内存使用情景:

1.单一数据结构频繁申请释放(具有固定长度和组织形式);

2.提前预知内存使用规模;

3.类似std::move的情景,“迁移内存”而不是“申请-释放-申请”。

内存管理造轮子的意义:

1.砍掉无用的算法和优化;

2.针对系统的硬件场景优化(到指令级);

3.减少系统调用的次数。

你可能感兴趣的:(linux,性能优化入门)