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进行大内存的分配。



猜你喜欢

转载自blog.csdn.net/YoungStunner/article/details/51261894