Nginx高效数据结构(5)——内存池(ngx_pool_t)

Nginx是我们学习编程的一个非常有参考价值的开源项目。良好的编码风格,高效的数据结构、架构设计。

快课网在此搜罗了一些优质资源。从本文开始讲述Nginx中常用的数据结构,主要包括Nginx的数组结构链表结构队列hash结构内存池等。

0. 

nginx对内存的管理由其自己实现的内存池结构ngx_pool_t来完成,本文重点叙述nginx的内存管理。

nginx内存管理相关文件:

(1) ./src/os/unix/ngx_alloc.h/.c

  • 内存相关的操作,封装了最基本的内存分配函数
  • free/malloc/memalign/posix_memalign,分别被封装为ngx_freengx_alloc/ngx_calloc, ngx_memalign
    • ngx_alloc:封装malloc分配内存
    • ngx_calloc:封装malloc分配内存,并初始化空间内容为0
    • ngx_memalign:返回基于一个指定alignment的大小为size的内存空间,且其地址为alignment的整数倍,alignment2的幂。

(2) ./src/core/ngx_palloc.h/.c

  • 封装创建/销毁内存池,从内存池分配空间等函数

.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4

 1. 内存池结构

nginx对内存的管理均统一完成,例如,在特定的生命周期统一建立内存池(main函数系统启动初期即分配1024B大小的内存池),需要内存时统一分配内存池中的内存,在适当的时候释放内存池的内存(如关闭http链接时调用ngx_destroy_pool进行销毁)

因此,开发者只需在需要内存时进行申请即可,不用过多考虑内存的释放等问题,大大提高了开发的效率。先看一下内存池结构。

1.1 ngx_pool_t结构

此处统一一下概念,内存池的数据块:即分配内存在这些数据块中进行,一个内存池可以有多一个内存池数据块。nginx的内存池结构如下。

其中,sizeof(ngx_pool_data_t)=16Bsizeof(ngx_pool_t)=40B
nginx将几乎所有的结构体放在ngx_core.h文件中重新进行了申明,如下。

1.2 其他相关结构

其他与内存池相干的数据结构,如清除资源的cleanup链表,分配的大块内存链表等,如下。

(gdb) p getpagesize()

$18 = 4096

全局变量ngx_pagesize的初始化是在如下函数中完成的。./src/os/unix/ngx_posix_init.c

这些数据结构之间的关系,请参考后面的图。

1.3 ngx_pool_t的逻辑结构

这些数据结构逻辑结构图如下。注:本文采用UML的方式画出该图。

pool01

2. 内存池操作

2.1 创建内存池

创建内存池有ngx_create_pool()函数完成,代码如下。

例如,调用ngx_create_pool(1024, 0x80d1c4c)后,创建的内存池物理结构如下图。

pool02

2.2 销毁内存池

销毁内存池由如下函数完成。

void ngx_destroy_pool(ngx_pool_t *pool)

该函数将遍历内存池链表,所有释放内存,如果注册了clenup(也是一个链表结构)亦将遍历该cleanup链表结构依次调用clenuphandler清理。同时,还将遍历large链表,释放大块内存。

2.3 重置内存池

重置内存池由下面的函数完成。

void ngx_reset_pool(ngx_pool_t *pool);

该函数将释放所有large内存,并且将d->last指针重新指向ngx_pool_t结构之后数据区的开始位置,同刚创建后的位置相同。

2.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);

返回值为分配的内存起始地址。选择其中的两个函数进行分析,其他的也很好理解,省略。

2.4.1 ngx_palloc()函数分析

ngx_palloc()代码如下,分析请参考笔者所加的注释。

例如,在2.1节中创建的内存池中分配200B的内存,调用ngx_palloc(pool, 200)后,该内存池物理结构如下图。

pool03

2.4.2 ngx_palloc_block()函数分析

ngx_palloc_block函数代码如下,分析请参考笔者所加的注释。

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

结合2.7节的内存池的物理结构,更容易理解。

2.5 释放内存

请参考如下函数,不再赘述。

ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p)

需要注意的是该函数只释放large链表中注册的内存,普通内存在ngx_destroy_pool中统一释放。

2.6 注册cleanup

请参考如下函数,该函数实现也很简单,此处不再赘述。

ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)

2.7 内存池的物理结构

针对本文第3节的例子,画出的内存池的物理结构如下图。

pool04

从该图也能看出2.4节的结论,即内存池第一块内存前40字节为ngx_pool_t结构,后续加入的内存块前16个字节为ngx_pool_data_t结构,这两个结构之后便是真正可以分配内存区域。

因此,本文Reference中的内存分配相关中的图是有一点点小问题的,并不是每一个节点的前面都是ngx_pool_t结构。

3. 一个例子

理解并掌握开源软件的最好方式莫过于自己写一些测试代码,或者改写软件本身,并进行调试来进一步理解开源软件的原理和设计方法。本节给出一个创建内存池并从中分配内存的简单例子。

3.1 代码

3.2 如何编译

这个问题是编写测试代码或者改写软件本身最迫切需要解决的问题,否则,编写的代码无从编译或运行,那也无从进行调试并理解软件了。我们要做的是学习这种编译工程的方法,针对该例子,笔者编写的makefile文件如下。

3.3 运行运行结果

 4. 小结

本文针对nginx-1.0.4的内存管理进行了较为全面的分析,包括相关内存池数据结构,内存池的创建、销毁,以及从内存池中分配内存等。最后通过一个简单例子向读者展示nginx内存池的创建和分配操作,同时借此向读者展示编译测试代码的方法。

分析完nginx的内存管理,你一定惊叹于nginx作者的聪明才智。这种内存管理的设计方法小巧、快捷,值得借鉴!

系列文章:

从开源代码Nginx中学习编码风格

nginx架构初探

Nginx高效数据结构(1)——数组(ngx_array_t)

Nginx高效数据结构(2)——链表(ngx_list_t)

Nginx高效数据结构(3)——队列(ngx_queue_t)

Nginx高效数据结构(4)——Hash表(ngx_hash_t)

Nginx高效数据结构(5)——内存池(ngx_pool_t)

作者:阿波

EDITED BY:快课(www.cricode.com)

本文链接:http://cricode.com/2999.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值