Nginx slab的实现 --- 第一篇“基本布局”

Nginx slab 用于进程间的共享内存

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

说明:本系列的文章基于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分配器的基本结构是怎么样的。

这里写图片描述

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';
}
初始化完成之后,整个内存结构布局就是这个样子滴:

这里写图片描述

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

有了这个图做铺垫,我们再讨论page页的分配时就容易多了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
通过把nginx slab的精简,把需要的头文件单独整理出来,增加了main方法,可以单独运行,代码包含了大量的中文注释,方便了了解和学习slab的运行机制 int main(int argc, char **argv) { ngx_log_t log; ngx_shm_t shm; ngx_slab_pool_t *shpool; char *ssl_session[10]; int n; ngx_pid = ngx_getpid(); memset(&log;, 0, sizeof(ngx_log_t)); memset(&shm;, 0, sizeof(ngx_shm_t)); shm.size = 512000; ngx_pagesize_shift = 0; ngx_pagesize = getpagesize(); for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ } printf("--%d\n", 1 << ngx_pagesize_shift); if (ngx_shm_alloc(&shm;) != NGX_OK) { return 1; } if (ngx_init_zone_pool(&log;, &shm;)) { return 1; } shpool = (ngx_slab_pool_t *) shm.addr; ssl_session[0] = (char *) ngx_slab_alloc_locked(shpool, 12); ssl_session[0] = (char *) ngx_slab_calloc(shpool, 56); ssl_session[1] = (char *) ngx_slab_alloc_locked(shpool, 14); ssl_session[2] = (char *) ngx_slab_alloc_locked(shpool, 11); ngx_slab_free(shpool, ssl_session[2]); ngx_slab_free(shpool, ssl_session[0]); ssl_session[2] = (char *) ngx_slab_alloc_locked(shpool, 65); return 0; } nginxslab的内存管理方式,这种方式的内存管理在nginx中,主要是与nginx 的共享内存协同使用的。nginxslab管理与linux的slab管理相同的地方在于均是利用了内存 的缓存与对齐机制,slab内存管理中一些设计相当巧妙的地方,也有一些地方个人感觉设计 不是很完美,或许是作为nginx设计综合考虑的结果。 nginx slab实现中的一大特色就是大量的位操作,这部分操作很多是与slot分级数组相关的。 为方便描述下面做一些说明: 1.将整个slab的管理结构称slab pool. 2.将slab pool前部的ngx_slab_pool_t结构称slab header. 3.将管理内存分级的ngx_slab_page_t结构称slot分级数组. 4.将管理page页使用的ngx_slab_page_t结构称slab page管理结构. 5.将具体page页的存放位置称pages数组. 6.将把page分成的各个小块称为chunk. 7.将标记chunk使用的位图结构称为bitmap.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值