• dlmalloc 2.8.6 源代码具体解释(6)


    本文章由vector03原创, 转载请注明出处.

    邮箱地址: mmzsmm@163.com, 欢迎来信讨论.

    3.4 sys_alloc

    sys_allocdlmalloc中向系统获取内存的主要接口. 因为涉及到mmap, top-most segment, top chunk的交互, 相对要更复杂. 我们相同先介绍主要分配算法, 再具体分析子函数.

    3.4.1 核心算法

    基本上sys_alloc分为四个步骤,

    1. 首先检查请求大小nb是否超出mmap_threshold的阈值. 假设是, 则放弃由分配器管理, 直接在mmap区开辟, 原因前面说过, 不再赘述.

    2. 依据mspace设定及当前top space的使用情况, 向系统申请一块适当的内存.

    Dlmalloc依照以下的顺序由主到次开辟,

    第一, 假设同意MORECORE, 则优先通过MORECORE开辟连续内存空间.

    连续空间开辟又分为例如以下几种情况,

    若当前mspace处于诞生阶段, 则直接开辟nb + SYS_ALLOC_PADDING大小的空间.

    若当前mspace已存在top, 则部分空间可利用top, 剩余nb – m->topsize + SYS_ALLOC_PADDING大小的空间向系统申请.

    MORECORE返回成功, 但空间不连续, 则会尝试扩展空间esize大小以满足分配需求. 假设空间扩展失败, 则反向MORECORE将之前申请的空间归还给系统.

    第二, 假设上一步申请失败, 或不同意MORECORE, 则通过MMAP申请. 注意, 这里的MMAPsys alloc步骤1mmap是两码事. 这一步申请的结果是要归入mspace空间的.

    第三, 倘若前两步都失败, 且同意非连续(noncontiguous)MORECORE, 则尝试直接在system heap上分配非连续空间. 这有些类似第一步中的扩展空间, 差别是此时已经明白top space不连续, 直接申请目标大小.

    3. 依据申请成功的地址与原top space的关系, 对连续空间合并. 假设不能合并, 则新开区段. 申请的地址和大小保存在tbasetsize暂时变量里.

    当中, 区段合并又分为两种. tbasetop-most区段末尾相毗邻, 则从后面合并. 这样的情况适用于大部分MORECORE以及小部分MMAP申请到的空间.

    tbasetop-most区段開始相毗邻, 则从前面合并. 这样的情况出现的比較少, 多在MMAP时产生, MORECORE尽管也可能出现该情况, 相对就更少.

    4. 最后从已扩展的top space中划分chunk返回给用户. 这样就完毕了sys_alloc的所有流程.

    具体代码凝视例如以下,

    这个代码基本上就是本小节開始时介绍的流程, 相信看懂了前面的算法说明, 这里自然没有什么难度. 须要说明的仅有两点,

    一个就是在Line4064Line4065出现的推断, 先对nbpadding计算得到asize, 再推断其是否小于等于nb. 这里就是溢出检測, 对于两个无符号整数的加法, 这是比較便捷的检验方法. 类似的代码在dlmalloc中到处都是.

    还有一个參考Line4108, 这里相同是溢出检測. 由于MORECORE的入參在dlmalloc中被觉得是有符号的. HALF_MAX_SIZE_Tsize_t的一半, 以此来推断ssize是否溢出.

    3.4.2 mmap_alloc

    nb大于mmap_threshold, 会调用该函数直接进行mmap分配. sys_alloc通过其它途径申请的差别在于, dlmalloc对这类空间倾向于不长期持有, 也不纳入不论什么分箱或区段中. 能够觉得它们是脱离dlmalloc管理的孤立内存区域.

    既然是孤立内存, 首尾就不会有毗邻的chunk, 但直接mmap出来的payload地址未必是对齐的, 因此在对齐后会产生内部碎片. dlmalloc就将这些碎片伪装成一个chunk. 这样, 当用户释放这片内存时, 能够依据记录在prev_foot中的size信息找到当初mmap出来的首地址.

    上图中, mmap分配的原始地址是mm, 经过对齐后的地址是p. dlmalloc将前面的对齐部分伪装成一个free chunk, 长度记录在p->prev_foot. 当释放时, 就能够依据payload指针又一次计算出mm的地址. 在结尾, 有长度为MMAP_FOOT_PAD的一段区域, 用来放置fake next chunk. 也就是保存magic以及fencepost.

    代码凝视例如以下,

    3.4.3 prepend_alloc

    3.4.1小节中介绍了从系统申请的扩展内存会依据其首地址和旧区段之间的位置关系做合并. 倘若append到区段后面, 申请内存是比較简单的, 由于扩展地址会直接补充到top, 仅仅需分割top就可以. 但假设prepend到前面情况就相对复杂了, 由于从原区段basetop之间的情况不明, 所以必须分情况讨论. prepend_alloc函数就是为此而写的.

    该函数会在一開始将分配请求从扩展空间中分割出来, 剩余工作就是依据不同情况对remainder做对应处理,

    1. 假设旧区段basetop是同一个地址, 直接移动top指针, remainder吸收到top.

    2. 假设旧区段basedv是同一地址, 则扩充dv的范围.

    3. 若旧区段開始是普通的free chunk, 则移动oldfirst指针, remainderfree chunk合并.

    4. 若旧区段開始是inused chunk, 则将remainder插入回分箱.

    代码凝视例如以下,


    3.4.4 add_segment

    对于无法合并的扩展内存区域, dlmalloc最后会将它们作为新的segment插入.

    创建新segment依照例如以下步骤进行,

    1. 首先, 依据top, 查找到当前top-most区段, 而且定位出在其结尾的隐藏chunk.

    2. top又一次初始化为新的segment的基址.

    3. mstate中保存的旧top-most区段信息push到旧区段的隐藏chunk. 并将新区段信息记录在mstate.

    4. 旧区段末尾写入一连串fenceposts.

    5. 若旧区段剩余的top可用, 则将旧top又一次插入分箱系统中.

    源代码凝视例如以下,

    Line4016top初始化函数, 该函数基本仅仅是简单的信息记录, 并在末尾伪装隐藏chunk, 代码例如以下,

  • 相关阅读:
    Redis常用数据类型及应用场景之Set
    Redis常用数据类型及应用场景之List
    Redis常用数据类型及应用场景之Hash
    exists & not exists
    oracle 中 dblink 的简单使用
    DockerCompose之数据卷Volume
    DockerCompose之常见编排脚本
    160308-学习State Pattern Actor
    12.3-框架维护
    12.2-机器人协作系统
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5103081.html
Copyright © 2020-2023  润新知