Nginx slab的实现 --- 第二篇“基于页的内存分配”

来源:互联网 发布:酷派手机自动开启数据 编辑:程序博客网 时间:2024/06/10 11:22

摘要: 本篇在上一篇“基本布局”的基础上介绍“基于页的内存分配”机制,也为后续讲解“基于块的内存分配”机制做个铺垫。

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

在上一篇中已经介绍了Nginx slab分配器的基本原理和内存空间布局,现在我们将在此基础上引入“基于页的内存分配”的相关内容。之所以这样安排是因为它的实现相对于“基于块的内存分配”要简单许多,同时它又是“基于块的内存分配”的基础,以它为突破口怎么看都是最好的选择:)

 在”基于页的内存分配“流程中只需要用到”page页内存管理单元“,而不涉及”分级内存管理单元“,为了方便讨论,我们将初始化后的内存布局图简化如下:

这里写图片描述

一、内存的分配

Nginx slab本身没有对外提供专门的按页分配内存的接口,具体采用哪种分配方式是在内部根据传入的size值并结合一定的算法来进行决策的。以ngx_slab_alloc为例:
void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size){    void  *p;    ngx_shmtx_lock(&pool->mutex);    p = ngx_slab_alloc_locked(pool, size);    ngx_shmtx_unlock(&pool->mutex);    return p;}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;    /*若要求分配的内存大小超过1/2页,则采用“按页分配”的方式*/    /*注意,这里的判断在最新的版本里好像已经改为">"了,因为1/2页本身就是一个管理分级*/    if (size >= ngx_slab_max_size) {        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,                       "slab alloc: %uz", size);        /*将size向上取整到页大小的整数倍上*/        page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)                                          + ((size % ngx_pagesize) ? 1 : 0));        if (page) {            p = (page - pool->pages) << ngx_pagesize_shift;            p += (uintptr_t) pool->start;        } else {            p = 0;        }        goto done;    }    ...    ...done:    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p);    return (void *) p;}/*“基于页的内存分配”函数实现,参数为要求分配的页数*/static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages){    ngx_slab_page_t  *page, *p;    /*“free”是空闲页管理单元链表头*/    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 {                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;}

下面这两幅图非常直观地说明了当初始化完成之后(假设此时共有N个空闲页),分别申请m(< N)页和N页内存时的情形。
这里写图片描述

这里写图片描述
下面再进一步来看一下当进行过若干次页分配后的内存空间布局,这里我们假设分配的顺序为:m0页、1页、m1页、1页:
这里写图片描述

有了上面的几幅图,再结合ngx_slab_alloc_pages()的源码,就很容易理解“基于页的内存分配”流程和实现机制了。

0 0
原创粉丝点击