Nginx内存池实现以及分析

Nginx内存池实现以及分析

开源案例

  • tcmalloc
  • jemalloc
  • nginx

为什么需要内存池

  • 为了解决在高并发下,需要不断申请内存和效率,造成了性能消耗,且造成了内存碎片化,不易统一管理。

内存池结构

struct mp_large_node
{
    struct mp_large_node *next; //链表
    void *data;                 // 指向大块存储空间的指针
};

struct mp_small_node
{
    struct mp_small_node *next; //指向下一个small_node

    unsigned char *last; //数据存储区起始的地址
    unsigned char *end;  //数据存储区最后的地址
    size_t failed;       //该块分配内存失败的次数
};

struct mp_pool
{
    size_t max;

    struct mp_small_node *current; // small_node 链表
    struct mp_large_node *large;   // large_node 链表
    struct mp_small_node head[0];  //柔性数组 实现可变结构体
};

图解

Nginx内存池实现以及分析_第1张图片

Nginx内存池实现以及分析_第2张图片

上图mp_node_s即为mp_small_node

  • pool指向一个包含了mp_pool,mp_small_node以及data大小和的内存空间。
  • small_node 指向一个包含了small_node以及data大小和的内存空间,通过next字段不断连接。
  • large_node 包含指向下一个large_node指针和指向具体数据的指针.

nginx内存池设计

  • 当我们分配较小内存片段时,通过small_node 管理。
  • 当我们分配较大内存片段室,通过large_node 管理。
  • 做到了内存复用,统一管理,不需要不断申请和销毁内存。
  • 内存池中分配的内存,最后统一销毁。

nginx内存池分配内存流程

Nginx内存池实现以及分析_第3张图片

内存池创建

struct mp_pool *mp_pool_create(size_t size)
{
    struct mp_pool *pool;
    int ret = posix_memalign((void **)&pool, MP_ALIGNMENT, size + sizeof(struct mp_small_node) + sizeof(struct mp_pool));
    if (ret)
    {
        printf("create error\n");
        return NULL;
    }

    pool->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
    pool->large = NULL;

    pool->current = pool->head;
    pool->head->last = (unsigned char *)pool + sizeof(struct mp_small_node) + sizeof(struct mp_pool);// 内存池中第一个内存段包含了pool+smallnode+data的大小
    pool->head->end = pool->head->last + size;
    pool->head->failed = 0;

    return pool;
}

内存池销毁

void mp_pool_destroy(struct mp_pool *pool)
{
    struct mp_large_node *large = pool->large;
    struct mp_small_node *nxt, *node;
    for (large = pool->large; large; large->next) //销毁所有large_data指向的数据
    {
        if (large->data)
        {
            free(large->data);
        }
    }
    node = pool->head->next;
    while (node) //销毁 所有small_node
    {
        nxt = node->next;
        free(node);
        node = nxt;
    }
    free(pool); //销毁内存池,pool指向了pool+small_node_data的数据大小
}

内存池分配内存

void *mp_malloc(struct mp_pool *pool, size_t size)
{
    if (size <= pool->max) //属于小内存
    {
        struct mp_small_node *n = pool->current;
        while (n)
        {
            if ((size_t)(n->end - n->last) >= size) //在其中一个小的内存片段中能够塞下需要的新内存大小
            {
                void *pos = n->last;
                n->last += size;
                return pos;
            }
            n = n->next;
        }
        return mp_malloc_small(pool, size); // 现有内存片段不够存储,搞一个新的内存片段
    }
    return mp_malloc_large(pool, size); //分配大内存
}

创建small_node

static void *mp_malloc_small(struct mp_pool *pool, size_t size)
{
    unsigned char *m;
    struct mp_small_node *head = pool->head; //pool中small_node的起始地址
    size_t psize = (size_t)(head->end - (unsigned char *)head); 
    int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize); //分配一个small_node 大小
    if (ret)
    {
        return NULL;
    }
    struct mp_small_node *newnode, *current, *p;
    newnode = (struct mp_small_node *)m;
    current = pool->current;
    p = current;
    while (p->next)
    {
        p = p->next;
    }//找到最后一个small_node
    p->next = newnode;
    newnode->next = NULL;
    newnode->failed = 0;
    newnode->last = m + size;
    newnode->end = m + psize;

    return m;
}

创建large_node

static void *mp_malloc_large(struct mp_pool *pool, size_t size)
{
    void *data = malloc(size);
    if (data == NULL)
        return NULL;
    struct mp_large_node *large = pool->large;
    while (large)
    {
        if (large->data == NULL) //如果指针为空,则指向新分配的内存空间
        {
            large->data = data;
            return data;
        }
    }
    struct mp_large_node *newnode = mp_malloc(pool, sizeof(struct mp_large_node)); //如果所有large_node 都占用了 就分配一新的
    if (newnode == NULL)
    {
        free(data);
        return NULL;
    }
    newnode->data = data;
    newnode->next = pool->large;
    pool->large = newnode; //头插法插入

    return data;
}

你可能感兴趣的:(c,后端)