#内存管理的艺术# 之 Nginx slab的实现 — 第一篇“基本布局”

#内存管理的艺术# 之 Nginx slab的实现 — 第一篇“基本布局”

访问这里,获取更多原创内容。

    说明:本系列的文章基于Nginx-1.5.0版本代码。

    Nginx slab分配器用于管理和分配小于一页的内存申请,但实际上大于一页的内存分配也是统一实现的,  具体代码在core/ngx_slab.c文件中,对应的头文件是core/ngx_slab.h。

    ngx_slab.h头文件中定义了两个重要的数据结构:

ngx_slab_pool_t;/*整个内存区的管理结构*/
ngx_slab_page_t;/*用于表示page页内存管理单元和slot分级管理单元*/

    同时还声明了对外提供的几个函数原型,分别是:

    用于初始化内存管理结构的:

void ngx_slab_init(ngx_slab_pool_t *pool);

    用于内存分配的:

void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size);
void *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size);
/*alloc 和 calloc的区别在于是否在分配的同时将内存清零*/

    用于内存释放的:

void ngx_slab_free(ngx_slab_pool_t *pool, void *p);
void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);

    这一次我们先从ngx_slab_init()函数开始,看看slab分配器的基本结构是怎么样的。

    由于slab管理和分配的内存是以2的幂次方为基准向上取整的,也就是说,如果你想申请一块20bytes的内存,那么就向上取整到32bytes。   

#内存管理的艺术# 之 Nginx slab的实现 --- 第一篇“基本布局”

    ngx_slab.c的实现中将内存分配分为两大类:

  • 基于页(page)的内存分配,由page页内存管理单元来进行管理,其实现相对简单,因为整个内存池的基本结构就是以页为单位进行划分的。

  • 基于块(chunk)的内存分配,将一页划分为若干块,实现相对复杂,除了page页内存管理单元外还引入了分级内存管理单元(slot数组)来共同管理;

    实际上,page页内存管理单元和slot分级管理单元都是由ngx_slab_page_t结构来表示的,slot分级管理数组紧跟在ngx_slab_pool_t结构之后,page页内存管理数组又紧跟在slot分级管理数组之后。

void
ngx_slab_init(ngx_slab_pool_t *pool)
{
    u_char            *p;
    size_t            size;
    ngx_int_t         m;
    ngx_uint_t        i, n, pages;
    ngx_slab_page_t   *slots;

    /* STUB */
    if (ngx_slab_max_size == 0) {
        /*slab分配器最大分配大小(slab分配器用于分配小于一页的内存申请,由于实际会以2的幂次方为基准向上取整,因此超过1/2页大小的内存申请也被取整为1页)*/
        ngx_slab_max_size = ngx_pagesize / 2;
        /*当使用bit位来标记块的使用情况时,如果想用一个uintptr_t类型的数来标记一整页中的所有块,则需要将一页分为(8 * sizeof(uintptr_t)个块,每块大小为ngx_slab_exact_size*/
        ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
        /*根据ngx_slab_exact_size计算对应的块大小移位ngx_slab_exact_shift*/
        for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
            /* void */
        }
    }
    /**/

    /*slab的最小分配单元,当申请的内存比min_size还小时,则取整到min_size;
    通常min_shift=3,则min_size=8*/
    pool->min_size = 1 << pool->min_shift;

    p = (u_char *) pool + sizeof(ngx_slab_pool_t);
    size = pool->end - p;/*除ngx_slab_pool_t之外的剩余空间大小*/

    ngx_slab_junk(p, size);

    /*slot分级管理数组的起始地址*/
    slots = (ngx_slab_page_t *) p;
    /*从最小块大小到页大小之间的分级数*/
    n = ngx_pagesize_shift - pool->min_shift;

    for (i = 0; i < n; i++) {
        slots[i].slab = 0;
        slots[i].next = &slots[i];/*表明分级数组中还没有要管理的页*/
        slots[i].prev = 0;
    }

    p += n * sizeof(ngx_slab_page_t);
    
    /*每一个实际的page页都对应一个页内存管理单元(后面会看到,反过来则不成立),这里会有疑问为什么size上没有减去slot分级数组占用的空间,下面会说明*/
    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));

    /*初始化页内存管理数组*/
    ngx_memzero(p, pages * sizeof(ngx_slab_page_t));

    pool->pages = (ngx_slab_page_t *) p;

    pool->free.prev = 0;
    pool->free.next = (ngx_slab_page_t *) p;

    /*页内存管理单元中的slab字段记录了其后跟随的连续空闲内存页数*/
    pool->pages->slab = pages;
    pool->pages->next = &pool->free;
    pool->pages->prev = (uintptr_t) &pool->free;

    /*将实际的page页起始地址对齐到pagesize*/
    pool->start = (u_char *)
                  ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),
                                ngx_pagesize);

    /*对齐后剩余的内存空间可能不足pages个页,需要进行调整;另外也可以看出实际的内存页数可能会少于page页管理单元的数目,多余的几个就空闲在最后好了,这也是为什么上面计算pages时size并没有减去slot分级数组大小的原因,因为一切都是由最终对齐后的内存空间大小决定的,所以前面也就不必要求那么精确了*/
    m = pages - (pool->end - pool->start) / ngx_pagesize;
    if (m > 0) {
        pages -= m;
        pool->pages->slab = pages;
    }

    pool->log_ctx = &pool->zero;
    pool->zero = '\0';
}

    初始化完成之后,整个内存结构布局就是这个样子滴:

004735_vI7v_2310891.png

    图中的各个标记基本保持与ngx_slab_init()函数中一致, 其中,N = pages – m,即经过对齐调整后的实际内存页数。

 

    有了这个图做铺垫,我们再讨论page页的分配时就容易多了。

 

 

 

转载于:https://my.oschina.net/u/2310891/blog/672539

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/109031.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 同济大学土木工程学院招收2名秋季入学全日制博士生

    同济大学土木工程学院招收2名秋季入学全日制博士生招生群体面向应届或往届硕士生,2020年秋季入学的博士生(全日制)研究方向智能建造与运营、人工智能与土木工程创新应用拟招人数2人招生要求具有本科和硕士学位,本科211或985;优…

    2022年7月25日
    21
  • 基于情感词典进行情感态度分析[通俗易懂]

    基于情感词典进行情感态度分析[通俗易懂]情感分析是指挖掘文本表达的观点,识别主体对某客体的评价是褒还是贬,褒贬根据进态度行倾向性研究。文本情感分析可以分为基于机器学习的情感分类方法和基于语义理解的情感分析。基于机器学习进行语义分析的话需要大量的训练集,同时需要人工对其进行分类标注。我所使用的方法是基于语义理解中的使用情感词典进行情感态度分析。下面是我所使用的情感词典:链接:HTTPS://pan.baidu.com/s/1xC…

    2022年8月23日
    4
  • epub精品电子书下载_丹尼尔·内格里诺

    epub精品电子书下载_丹尼尔·内格里诺郑重声明:本号收录的电子书均来源于互联网或网友分享,链接内容仅作分享交流学习使用,不用于任何商业用途,版权归原作者和出版社所有,如果喜欢,请支持和购买正版,谢谢。下载地址:http://pan.63

    2022年8月3日
    3
  • 各代iphone尺寸_iphone屏幕尺寸比例是多少,iphone各代屏幕尺寸大小介绍

    各代iphone尺寸_iphone屏幕尺寸比例是多少,iphone各代屏幕尺寸大小介绍自2007年苹果发布第一代iPhone以来,到目前最新的iPhoneXS、XSMax,苹果累计发布了21款手机,涉及7款尺寸,8种分辨率。那么这么多的不同手机之间有什么内在联系吗?为什么在iPhone6代-8代之间,屏幕是4.7寸和5.5寸,而iPhoneXS和Max分别是5.8寸和6.5寸,他们之间有什么联系吗?XR为什么分辨率这么低,被吐槽是大果粒屏?仔细看完本文,你将对iPhone的设…

    2022年5月14日
    46
  • java最简单最全入门基础笔记(不简单,不全你打我)「建议收藏」

    java最简单最全入门基础笔记(不简单,不全你打我)「建议收藏」刚开始学习java的时候,可能是一头雾水,不知道从何学起,这里跟大家分享一下!(仅仅只供初级学者学习,大佬勿喷)一、初始java1.生活中的程序:从起床到教室上课的过程穿衣打扮》起床》洗漱》出宿舍》》吃早餐》到教室按照特定的顺序去完成某一件事的过程我们叫做生活中的程序2.计算机中的程序:一组有序指令的集合,需要和计算机交流就要使用计算机语言,java就是计算机语言的一种3.java…

    2022年6月4日
    30
  • Linux基础命令—resize2fs

    resize2fs    调整ext2\ext3\ext4文件系统的大小,它可以放大或者缩小没有挂载的文件系统的大小。如果文件系统已经挂载,它可以扩大文件系统的大小,前提是内核支持在线调整大小。    size参数指定所请求的文件系统的新大小。如果没有指定任何单元,那么size参数的单位应该是文件系统的文件系统块大小。size参数可以由下列单位编号之一后缀:“s”、“K”、…

    2022年4月5日
    298

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号