Nginx中slab分配大内存时需要注意的地方

nginx一般使用slab来管理共享内存,在程序启动时,分配好需要共享的内存,然后使用slab来进行初始化,之后就交给slab来管理这段内存。但是在大内存分配时,可能会出现分配不出内存的现象。

因为分配内存时,会调用ngx_slab_alloc_locked,在这个函数里面会先判断size是否大于ngx_slab_max_size,代码如下。

void *
ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)
{
    size_t            s;
    uintptr_t         p, n, m, mask, *bitmap;
    ngx_uint_t        i, slot, shift, map;
    ngx_slab_page_t  *page, *prev, *slots;

    /* 判断大小 */
    if (size >= ngx_slab_max_size) {

        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
                       "slab alloc: %uz", size);

        /* 直接分配页 */
        page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1)
                >> ngx_pagesize_shift);
        if (page) {
            p = (page - pool->pages) << ngx_pagesize_shift;<span style="white-space:pre">		</span>
            p += (uintptr_t) pool->start;

        } else {
            p = 0;
        }

        goto done;
    }

    ...

}
注意因为ngnix中对页的对小内存块的分配是以2的倍数为基础的,所以使用ngx_pagesize_shift变量来指定页大小对应的位移变量,这样可以快速的使用位操作。

ngx_slab_max_size在nginx调用ngx_slab_init的时候初始化为ngx_pagesize / 2。我们知道,slab会将整块的内存分成pages,每个pages大小为ngx_pagesize,slab在分配小内存时,会将一个page拆分成多个小块进行分配,而如果我们分配的内存大于ngx_pagesize / 2时,slab是没办法进行拆分的,所以当我们分配的内存大于ngx_slab_max_size时,因为不需要进行拆分,所以直接分配页内存就可以了。所以这里直接调用ngx_slab_alloc_pages来分配内存。ngx_slab_alloc_pages的代码如下:

static ngx_slab_page_t *
ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)
{
    ngx_slab_page_t  *page, *p;

    for (page = pool->free.next; page != &pool->free; page = page->next) {

        /* 判断当前页还能分配多少连续的页 */
        if (page->slab >= pages) {

            if (page->slab > pages) {
                /* 重新设置剩下还能分配的连续空间 */
                page[pages].slab = page->slab - pages;
                page[pages].next = page->next;
                page[pages].prev = page->prev;

                p = (ngx_slab_page_t *) page->prev;
                p->next = &page[pages];
                page->next->prev = (uintptr_t) &page[pages];

            } else {
                /* 剩下连续的pages正好够用 */
                p = (ngx_slab_page_t *) page->prev;
                p->next = page->next;
                page->next->prev = page->prev;
            }

            page->slab = pages | NGX_SLAB_PAGE_START;
            page->next = NULL;
            page->prev = NGX_SLAB_PAGE;

            /* 如果只需要分配一个页,则直接返回 */
            if (--pages == 0) {
                return page;
            }

            /* 否则将剩下所需要的页设置占用标记 */
            for (p = page + 1; pages; pages--) {
                p->slab = NGX_SLAB_PAGE_BUSY;
                p->next = NULL;
                p->prev = NGX_SLAB_PAGE;
                p++;
            }

            return page;
        }
    }

    ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory");

    return NULL;
}

上面的代码中我们可以看到,在空闲页中p->slab用于标记剩下连续,连接页的第一个页会设置这个值。所以在slab初始化之后,第一个页的slab被赋值为所有页的数量。在使用过程中,由于经常alloc与free,会造成连续空闲页变得断断续续,当没有连续的所需要的空闲页进行分配时,就会出现内存无法分配的问题。所以,使用slab进行大内存分配时,就会出现内存无法分配的现象。所以,我们在使用中,应该避免使用slab进行大内存的分配。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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; } nginx的slab内存管理方式,这种方式的内存管理在nginx,主要是与nginx 的共享内存协同使用的。nginx的slab管理与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、付费专栏及课程。

余额充值