zoukankan      html  css  js  c++  java
  • nginx源码剖析(3)nginx中的内存池

    1.为什么需要内存池

        为什么需要内存池?

    a. 在大量的小块内存的申请和释放的时候,能更快地进行内存分配(对比malloc和free)

    b.减少内存碎片,防止内存泄露。

    2.内存池的原理

        内存池的原理非常简单,用申请一块较大的内存来代替N多的小内存块,当有需要malloc一块

    比较小的内存是,直接拿这块大的内存中的地址来用即可。

        当然,这样处理的缺点也是很明显的,申请一块大的内存必然会导致内存空间的浪费,但是

    比起频繁地malloc和free,这样做的代价是非常小的,这是典型的以空间换时间。

        一个典型的内存池如下图所示:

    MemoryPool_Step5

     

     

     

     

     

     

     

                     图一:一个典型的内存池。

     

        首先定义这样一个结构体:

     

        一个内存池就是这样一连串的内存块组成。当需要用到内存的时候,调用此内存池定义好的接口

    GetMemory(),而需要删除的时候FreeMemory()。

        而GetMemory和FreeMemory干了什么呢?GetMemory只是简单返回内存池中可用空间的地址。

    而FreeMemory干了两件事情:一: 改变UsedSize 的值,二:重新初始化这一内存区域。

    3.nginx中的内存池

    3.1 nginx内存池的结构表示

        首先我们看一下nginx内存池的定义:

    代码
    struct ngx_pool_s {
    ngx_pool_data_t d;
    //表示数据区域
    size_t max;//内存池能容纳数据的大小
    ngx_pool_t * current;//当前内存池块(nginx中的内存池是又一连串的内存池链表组成的)
    ngx_chain_t* chain;//主要为了将内存池连接起来
    ngx_pool_large_t* large;//大块的数据
    ngx_pool_cleanup_t* cleanup;//清理函数
    ngx_log_t* log;//写log
    };

    nginx中的内存池和普通的有比较大的不同。nginx中的内存池是由N个内存池链表

    组成的,当一个内存池满了以后,就会从下一个内存池中提取空间来使用。 

      

    对于ngx_pool_data_t的定义非常简单

    typedef struct {
    u_char
    *last;
    u_char
    *end;
    ngx_pool_t
    *next;
    ngx_uint_t failed;
    } ngx_pool_data_t;

    其中last表示当前数据区域的已经使用的数据的结尾。

    end表示当前内存池的结尾。

    next表示下一个内存池,前面已经说过,再nignx中,当一个内存池空间

    不足的时候,它不会扩大其空间,而是再新建一个内存池,组成一个内存池链表。

    failed标志申请内存的时候失败的次数。

    在理解了这个结构体后面的就非常简单了。

    current 表示当前的内存池。

    chain表示内存池链表。

    large表示大块的数据。

    对于ngx_pool_large_t定义如下:

     

    struct ngx_pool_large_s {
    ngx_pool_large_t
    * next;
    void* alloc;
    };

    此结构体的定义也是非常简单的。一个内存地址的指针已经指向下一个地址的指针。

    这里再解释下为什么需要有large数据块。当一个申请的内存空间大小比内存池的大小还要大的时候,

    malloc一块大的空间,再内存池用保留这个地址的指针。


    Cleanup保持存着内存池被销毁的时候的清理函数。

    typedef void (*ngx_pool_cleanup_pt)(void *data);
    struct ngx_pool_cleanup_s {
    ngx_pool_cleanup_pt handler;
    void* data;
    ngx_pool_cleanup_t
    * next;
    };

    ngx_pool_cleanup_pt 是一个函数指针的典型用法,

    在这个结果中保存这需要清理的数据指针以及相应的清理函数, 让内存池销毁

    或其他需要清理内存池的时候,可以调用此结构体中的handler。

        下面是我画的一张nginx的内存池的结构图。

                            ngx_pool
                                                            <图1. ngx_pool 结构体>

    3.2 nginx内存池源代码分析

        要大体了解一个内存池,只需要了解其池子的创建,内存的分配以及池子的销毁即可。下面就分析下

    ngx_pool_t的这个3个方面。注:其中有些代码可能与ngx_pool中的源代码有所差异,但是整体意思

    绝对是一样的,本人的修改,只是为了更好的分析,比如 我就把所有写log的过程都去掉了。

    3.2.1 ngx_create_pool

    创建一个内存池

     


        nginx内存池的创建非常简单,申请一开size大小的内存,把它分配给 ngx_poo_t。

    3.2.2 ngx_palloc

    从内存池中分配内存.

     

        这个函数从内存池用拿出内存,如果当前内存池已满,到下一个内存池,如果所有的内存池已满,

    增加一个新的内存池,如果申请的内存超过了内存池的最大值,从*large中分配

    3.3.3  ngx_destroy_pool

    内存池的销毁

     

        销毁一个内存池其实就是干了三件事, 调用清理韩式, 释放大块的内存,释放内存池,需要注意的

    一点是在nginx中, 小块内存除了在内存池被销毁的时候都是不能被释放的。

    3.3.4 ngx_palloc_block

        前面说过,在nginx中,当内存池满了以后,会增加一个新的内存池。这个动作就是靠ngx_palloc_block

    函数实现的。

     

        这个函数就是申请了一块内存区域,变为一个内存池,然后把它连接到原来内存池的末尾。

    3.3.5 ngx_palloc_large 和ngx_pfree

        在nginx中,小块内存除了在内存池销毁之外是不能释放的,但是大块内存却可以,这两个

    函数就是用来控制大块内存的申请和释放, 代码也非常简单,调用malloc申请内存,连接到

    ngx_pool_large_t中 和 调用free释放内存。这里就不贴上代码了。

    4. 小结

        不知不觉,写了快一个下午的时间了,真快啊。

         nginx的内存池的代码也先介绍到这里,其实nginx内存池功能强大,所以代码也比较复杂,

    这里只是列出了内存池的大体流程,还有很到一部分代码未列出来。

  • 相关阅读:
    R中character和factor的as.integer的不同
    ggplot2练习
    R的plotmath
    Python数据科学手册(2) NumPy入门
    Python数据科学手册(1) IPython:超越Python
    ggplot2(6) 标度、坐标轴和图例
    R自带数据集
    ggplot2(5) 工具箱
    MATLAB神经网络(7) RBF网络的回归——非线性函数回归的实现
    ggplot2(4) 用图层构建图像
  • 原文地址:https://www.cnblogs.com/sld666666/p/1766255.html
Copyright © 2011-2022 走看看