nginx的内存池及内存管理

nginx对内存的管理是由自己实现的内存池结构ngx_pool_t来完成,本文主要讲nginx的内存管理。

nginx对内存管理涉及到四个文件:src/core/ngx_palloc.h、src/core/ngx_palloc.c、src/os/unix/ngx_alloc.c、src/os/unix/ngx_alloc.h

ngx_alloc.h和ngx_alloc.c文件主要是对malloc、calloc、free、posix_memalign和memalign的封装,分别被封装为ngx_alloc、ngx_calloc、ngx_free和ngx_memalign

ngx_alloc:封装malloc分配内存

ngx_calloc:封装malloc分配内存,并初始化为0

ngx_memalign:返回基于alignment对齐的大小为size的内存空间,即地址为alignment的整数倍,也是2的幂

ngx_palloc.c文件中主要是对内存池的创建及销毁,从内存池中分配内存。

内存池结构

nginx对内存的管理统一完成,在nginx启动时,分配的是1024B大小的内存池,需要内存时从内存池中分配内存,在适当的时候释放内存池的内存(如关闭http连接时调用ngx_destroy_pool)。nginx中内存池结构定义为ngx_pool_t,其内存池结构如下。

typedef struct {
    u_char               *last;		//内存池中的内存分配到此处,也是下一次分开始位置
    u_char               *end;	 	//内存池的结束地址
    ngx_pool_t           *next;		//指向下一块内存,也可以称为内存池
    ngx_uint_t            failed;	//分配失败的次数
} ngx_pool_data_t;					//内存池中数据的信息


struct ngx_pool_s {
    ngx_pool_data_t       d;	//内存池中的数据块
    size_t                max; //内存池中用于存放数据的容量
    ngx_pool_t           *current; //指向当前内存池
    ngx_chain_t          *chain;  //指向下一个ngx_chain_t结构
    ngx_pool_large_t     *large;   //大块内存链表,即内存大小大于max
    ngx_pool_cleanup_t   *cleanup; //释放内存池的回调
    ngx_log_t            *log;//日志信息
};
像还有其它一些结构,如清除操作、大块数据链表,如下

#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)

#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)

#define NGX_POOL_ALIGNMENT       16
#define NGX_MIN_POOL_SIZE                                                     \
    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \
              NGX_POOL_ALIGNMENT)


typedef void (*ngx_pool_cleanup_pt)(void *data);  //清除操作的回调

typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;

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


typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    ngx_pool_large_t     *next;    //指向下一个大块内存
    void                 *alloc;   //指向分配的大块内存
};
内存池操作

1、内存池创建

创建内存池由函数ngx_create_pool来完成。代码如下

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
    ngx_pool_t  *p;

    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
        return NULL;
    }

    p->d.last = (u_char *) p + sizeof(ngx_pool_t);		//指向ngx_pool_t结构之后,也以下次内存分配的开始
    p->d.end = (u_char *) p + size;						//指向内存池的结束位置
    p->d.next = NULL;
    p->d.failed = 0;

    size = size - sizeof(ngx_pool_t);					//指定的分配大小减去ngx_pool_t结构体的大小(40B)
    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;  //最大不会超过4095

    p->current = p;
    p->chain = NULL;
    p->large = NULL;
    p->cleanup = NULL;
    p->log = log;

    return p;
}
2、内存池销毁

销毁内存池由函数ngx_destroy_pool完成。

遍历cleanup链表,如果有回调函数,就回调处理;遍历large大内存链表,释放分配的大块内存;遍历数据块的链表,将分配的小块内存释放。

3、内存池重置

重置由函数ngx_reset_pool完成。
释放large链表的大块内存,将数据块的last指针置为ngx_pool_t后,与创建时的位置相同。

4、分配内存

分配内存的函数有四个

void *ngx_palloc(ngx_pool_t *pool, size_t size)
void *ngx_pnalloc(ngx_pool_t *pool, size_t size)
void *ngx_pcalloc(ngx_pool_t *pool, size_t size)
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)

ngx_palloc代码分析如下

void *ngx_palloc(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    ngx_pool_t  *p;
	
	//分配的大小不大于内存池的容量
    if (size <= pool->max) {

        p = pool->current;

        do {
            m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT); //将下一次分配的地址NGX_ALIGNMENT对齐

			//内存池中的剩余大小可以装下size
            if ((size_t) (p->d.end - m) >= size) {
                p->d.last = m + size;

                return m;
            }

            p = p->d.next;

        } while (p);

		//在已有的内存池中没有找到可以分配size大小的空间,就再申请一个内存块。
        return ngx_palloc_block(pool, size);
    }
	
	//分配的大小大于内存池的容量,就大块内存分配
    return ngx_palloc_large(pool, size);
}
ngx_palloc_block函数分析如下

void *ngx_palloc_block(ngx_pool_t *pool, size_t size)
{
    u_char      *m;
    size_t       psize;
    ngx_pool_t  *p, *new;

    psize = (size_t) (pool->d.end - (u_char *) pool);   //计算内存池的大小 

    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);  //分配一个与内存池一样大小的内存块
    if (m == NULL) {
        return NULL;
    }

    new = (ngx_pool_t *) m;

    new->d.end = m + psize;	//与创建内存池一样,设置内存块的结束位置
    new->d.next = NULL;
    new->d.failed = 0;

    m += sizeof(ngx_pool_data_t);
    m = ngx_align_ptr(m, NGX_ALIGNMENT);
    new->d.last = m + size;  //设置当前内存块的使用到的位置,即下一次分配内存的开始

    for (p = pool->current; p->d.next; p = p->d.next) {
        if (p->d.failed++ > 4) {
            pool->current = p->d.next;			//设置内存池的current指针
        }
    }

    p->d.next = new;

    return m;
}
注意:该函数分配一块内存后,last指针指向的是ngx_pool_data_t结构体(16B)之后数据区的起始位置,而创建内存池, 指向的是ngx_pool_t结构体之后数据区的起始位置。

5、释放内存

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)

该函数只是释放large链表中分配的内存。

6、注册cleanup

由函数ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)完成。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Nginx的反向代理 http://www.cnblogs.com/zhrxidian/p/5491285.html tomcat+nginx+redis实现均衡负载、session共享(二) 今天我们接着说上次还没完成session共享的部分,还没看过上一篇的朋友可以先看下上次内容,http://www.cnblogs.com/zhrxidian/p/5432886.html。 1.redis简介及下载安装 作为这次的主角,相信大家对redis应该都一定印象,redis是一款开源的高性能key-value数据库,拥有丰富的键值储存类型,并提供多种语言的API。 与一般数据库不同,redis是使用内存作为主存,而使用硬盘来实现数据持久化,而且redis是周期性的将数据写到硬盘上。这就意味着一旦我们服务器出现断电、重启之类的情况,我们很可能会出现数据丢失的情况,所以不建议使用redis来存放关键的数据。当然,也正因为redis读写数据都使用内存,所以它的速度是非常快的,很适合我们来存放一些临时性的数据。 此外,redis能实现的作用很多,诸如队列、缓存之类的,但我也还没使用过,无法在这里为大家说明,但不影响我们今天的session共享功能。 首先我们先下载redis,这是windows版本的下载地址 https://github.com/ServiceStack/redis-windows。 可以点击右边下载全部文件,但感觉没必要,而且下载速度偏慢,建议进入downloads里面下载我们所需的redis包即可。 redis在windows下是免安装的,下载完成后,解压,将文件夹复制到自己某个盘中就好了。解压后是这个样子的。 一开始redis是默认不需要密码,如果想要设置密码,可以进入redis.windows.conf文件下找到requirepass,删除前面的#号,在其后面便可以设置密码,我这里设成了123456。 下面我们打开redis。首先打开cmd,进入我们redis目录下,输入redis-server.exe redis.windows.conf。出现下面界面,则打开成功。(不可偷懒想双击redis-server.exe完事,这样虽然也能打开,但不会加载配置文件)。 另外和别的数据库一样,我们需要安装一个辅助的可视化工具Redis Desktop Manager,这是下载地址:http://redisdesktop.com/download。 我们下载windows版本,安装完成后,还没有任何连接对象,那就让我们给它添加一个。点击下方的connect to redis server,出现个弹出框。然后Name我们可以随便输,Host添我们redis服务器的ip地址,本地可以直接填写localhost,端口默认为6379,Auth就是密码,不是必填项,如果没设置密码可不填,点击OK,建立完成。 至此,我们的准备工作都已经完成,下面开始我们的spring与redis的整合之旅。 2.Spring与Redis的整合之旅 这是上篇文章结束时我的项目目录。 其实Spring本身就有提供对redis的支持,就是spring-session,我们只需将这个在pom.xml添加如下代码,maven便会自己下载所需的jar包及依赖包。 1 <dependency>2 <groupId>org.springframework.session</groupId>3 <artifactId>spring-session-data-redis</artifactId>4 <version>1.1.1.RELEASE</version>5 <type>pom</type>6 </dependency> 随后我们在resources文件夹中新建一个redis.properties,往里面添加如下内容。 再新建一个spring-redis.xml,往里面添加我们redis相关的配置。其中maxInactiveIntervalInSeconds是设置session有效时间,以秒为单位,但实际上无论怎么设,session真实有效时间还是会比我们设置的稍微长一些。 1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http:/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kgduu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值