【nginx源码学习与运用 一】内存池结构ngx_pool_t

最近工作不是很忙,利用部分闲余时间了解了下nginx。
在写这篇博客的时候仅仅距离我学习nginx不到一周的时间,所以我完全是站在一个新手的角度上,arvik一边学习nginx,一边尝试把学到的东西能通过实战运用的方式讲解出来供新手参考。

在 【nginx源码学习与运用】系列博客中arvik将在csdn的代码托管服务器CODE上创建测试项目来演示实例,地址https://code.csdn.net/u012819339/nginx_study ,你可以将其自由的下载到本地,或者通过git来实时获取更新

arvik是在32位ubuntu上进行示例编写的,如果你的机器是64位机器,请将项目中ak_core/ak_config.h文件第15行进行如下更改:

//更改前:

#define machine_32_bit

//更改后:

//#define machine_32_bit

第一篇学习的是内存池,在nginx中几乎所有的对象内存分配都是在内存池的基础上实现的,内存池作为nginx中基础的数据结构我认为首先应该被学习,然后再一步步学习其它的基础模块。


内存池

成员结构

//该结构用来描述小块内存
typedef struct {
    u_char               *last; //指向未分配空间的首地址
    u_char               *end; //指向小块内存池尾部
    ngx_pool_t           *next; //单向链表指针,指向下一个小块内存结构
    ngx_uint_t            failed; //申请内存失败次数,大于4将导致ngx_pool_t中的curent指向下一个小块内存结构
} ngx_pool_data_t;


struct ngx_pool_s {
    ngx_pool_data_t       d;
    size_t                max; //小块内存与大块内存的分界线,对象分配内存超过该值即视为大块内存
    ngx_pool_t           *current; //指向当前可以分配小块内存的ngx_pool_t结构,
    ngx_chain_t          *chain; //缓冲链,用于将内存串接起来,在nginx的filter模块中较多使用,目前与内存池关系不大,先略过
    ngx_pool_large_t     *large; //指向大块内存结构链表的首部
    ngx_pool_cleanup_t   *cleanup;//清理资源的回调函数
    ngx_log_t            *log; //日志
};

操作方法

函数 解释
ngx_create_pool 参数size并不是可用空间,初次分配后可用空间 = size - sizeof(ngx_pool_t)
ngx_destroy_pool 销毁内存池,同时调用清理函数(由ngx_pool_cleanup_add方法添加的方法)清理资源
ngx_reset_pool 重置内存池,将大块内存归还给操作系统,小块内存释放后继续使用

ngx_pool_t内存池示意图

【nginx源码学习与运用 一】内存池结构ngx_pool_t_第1张图片

使用方法与注意事项

  1. 内存池的生存周期,这个非常重要!!!
    如果一个内存池的生存周期非常长,而在该内存池生存周期内有频繁的申请释放某些对象(比如数组对象),或申请临时对象,则会对资源造成极大的浪费,只有ngx_pool_t结构被销毁的时候这些内存才真正的被释放出来,希望筒子们多多参看源码!在此举个例子,我们来看看nginx动态数组的destroy函数:
void
ngx_array_destroy(ngx_array_t *a)
{
    ngx_pool_t  *p;

    p = a->pool;

    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
        p->d.last -= a->size * a->nalloc;
    }

    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {
        p->d.last = (u_char *) a;
    }
}

该函数在绝大多数情况下什么都不会做,释放了一个数组对象后,给它提供内存的内存池几乎不会有察觉或任何改变,故该数组对象实际上占用的空间没有真正的被释放出来,这个空间也不可能再被分配给其它对象,除非重置或销毁ngx_pool_t内存池。
nginx之所以能用这样的内存池设计是因为nginx是一个纯粹的web服务器,与客户端的每一个tcp链接都有明确的短暂的生存周期!

示例代码

arvik将nginx中的部分基础结构代码提出来了,好作为新手学习练习使用。见 https://code.csdn.net/u012819339/nginx_study
main.c

/*
blog:   http://blog.csdn.net/u012819339
email:  [email protected]
author: arvik
*/

#include <stdio.h>
#include "ak_core.h"
#include "pt.h"

int main()
{
    ngx_pool_t *p, *p2;

    p = ngx_create_pool(NGX_DEFAULT_POOL_SIZE); //16KB
    if(p == NULL)
        return -1;

    PT_Info("pool p info:\nlast:%x  end:%x  max:%u  current:%x   realsize:%u\n", 
            p->d.last, p->d.end, p->max, p->current, p->d.end - p->d.last);

    p2 = ngx_create_pool(100); //大小不能小于sizeof(ngx_pool_t),否则会发生指针越界
    if(p2 == NULL)
        return -2;

    PT_Info("pool p2 info:\nlast:%x end:%x  max:%u  current:%x   realsize:%u\n", 
            p2->d.last, p2->d.end, p2->max, p2->current, p2->d.end - p2->d.last);

    ngx_destroy_pool(p);
    ngx_destroy_pool(p2);
}

运行结果
截图如下:
【nginx源码学习与运用 一】内存池结构ngx_pool_t_第2张图片


点击调转到【nginx源码学习与运用 一】代码地址

你可能感兴趣的:(nginx,nginx源码学习与运用)