Chinaunix首页 | 论坛 | 博客
  • 博客访问: 293851
  • 博文数量: 134
  • 博客积分: 667
  • 博客等级: 上士
  • 技术积分: 770
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-08 15:19
文章分类

全部博文(134)

文章存档

2012年(134)

分类: LINUX

2012-06-12 15:33:05

Content

0.

1. 数组结构

1.1 ngx_array_t结构

1.2 ngx_array_t的逻辑结构

2. 数组操作

2.1 创建数组

2.2 销毁数组

2.3 添加1个元素

3. 一个例子

3.1 代码

3.2 如何编译

3.3 运行结果

4. 小结

0. 序

本文开始介绍nginx的容器,先从最简单的数组开始。

数组实现文件:文件:./src/core/ngx_array.h/.c。.表示nginx-1.0.4代码目录,本文为/usr/src/nginx-1.0.4。

1. 数组结构

1.1 ngx_array_t结构

nginx的数组结构为ngx_array_t,定义如下。


点击(此处)折叠或打开

  1. struct ngx_array_s {
  2.     void *elts; //数组数据区起始位置
  3.     ngx_uint_t nelts; //实际存放的元素个数
  4.     size_t size; //每个元素大小
  5.     ngx_uint_t nalloc; //数组所含空间个数,即实际分配的小空间的个数
  6.     ngx_pool_t *pool; //该数组在此内存池中分配
  7. };

  8. typedef struct ngx_array_s ngx_array_t;

struct ngx_array_s { void *elts; //数组数据区起始位置 ngx_uint_t nelts; //实际存放的元素个数 size_t size; //每个元素大小 ngx_uint_t nalloc; //数组所含空间个数,即实际分配的小空间的个数 ngx_pool_t *pool; //该数组在此内存池中分配 }; typedef struct ngx_array_s ngx_array_t;

sizeof(ngx_array_t)=20。由其定义可见,nginx的数组也要从内存池中分配。将分配nalloc个大小为size的小空间,实际分配的大小为(nalloc * size)。详见下文的分析。

1.2 ngx_array_t的逻辑结构

ngx_array_t结构引用了ngx_pool_t结构,因此本文参考nginx-1.0.4源码分析—内存池结构ngx_pool_t及内存管理一文画出相关结构的逻辑图,如下。注:本文采用UML的方式画出该图。

2. 数组操作

数组操作共有5个,如下。


 

点击(此处)折叠或打开

  1. //创建数组
  2. ngx_array_t*ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);

  3. //销毁数组
  4. voidngx_array_destroy(ngx_array_t *a);

  5. //向数组中添加元素
  6. void*ngx_array_push(ngx_array_t *a);
  7. void*ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);

  8. //初始化数组
  9. staticngx_inline ngx_int_t
  10. ngx_array_init(ngx_array_t*array, ngx_pool_t *pool, ngx_uint_t n, size_t size)


 

//创建数组 ngx_array_t*ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size); //销毁数组 voidngx_array_destroy(ngx_array_t *a); //向数组中添加元素 void*ngx_array_push(ngx_array_t *a); void*ngx_array_push_n(ngx_array_t *a, ngx_uint_t n); //初始化数组 staticngx_inline ngx_int_t ngx_array_init(ngx_array_t*array, ngx_pool_t *pool, ngx_uint_t n, size_t size)因实现都很简单,本文简单分析前3个函数。

2.1 创建数组

创建数组的操作实现如下,首先分配数组头(20B),然后分配数组数据区,两次分配均在传入的内存池(pool指向的内存池)中进行。然后简单初始化数组头并返回数组头的起始位置。


点击(此处)折叠或打开

  1. ngx_array_t*
  2. ngx_array_create(ngx_pool_t*p, ngx_uint_t n, size_t size)
  3. {
  4.     ngx_array_t *a;

  5.     a = ngx_palloc(p,sizeof(ngx_array_t)); //从内存池中分配数组头
  6.     if (a == NULL) {
  7.         return NULL;
  8.     }

  9.     a->elts = ngx_palloc(p,n * size); //接着分配n*size大小的区域作为数组数据区
  10.     if (a->elts == NULL) {
  11.         return NULL;
  12.     }

  13.     a->nelts = 0; //初始化
  14.     a->size = size;
  15.     a->nalloc = n;
  16.     a->pool = p;

  17.     return a; //返回数组头的起始位置
  18. }

ngx_array_t* ngx_array_create(ngx_pool_t*p, ngx_uint_t n, size_t size) { ngx_array_t *a; a = ngx_palloc(p,sizeof(ngx_array_t)); //从内存池中分配数组头 if (a == NULL) { return NULL; } a->elts = ngx_palloc(p,n * size); //接着分配n*size大小的区域作为数组数据区 if (a->elts == NULL) { return NULL; } a->nelts = 0; //初始化 a->size = size; a->nalloc = n; a->pool = p; return a; //返回数组头的起始位置 }创建数组后内存池的物理结构图如下。

2.2 销毁数组

销毁数组的操作实现如下,包括销毁数组数据区和数组头。这里的销毁动作实际上就是修改内存池的last指针,并没有调用free等释放内存的操作,显然,这种维护效率是很高的。


 

点击(此处)折叠或打开

  1. void
  2. ngx_array_destroy(ngx_array_t*a)
  3. {
  4.     ngx_pool_t *p;

  5.     p = a->pool;

  6.     if ((u_char *) a->elts+ a->size * a->nalloc == p->d.last) { //先销毁数组数据区
  7.         p->d.last -=a->size * a->nalloc; //设置内存池的last指针
  8.     }

  9.     if ((u_char *) a +sizeof(ngx_array_t) == p->d.last) { //接着销毁数组头
  10.         p->d.last = (u_char*) a; //设置内存池的last指针
  11.     }
  12. }


 

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; //设置内存池的last指针 } if ((u_char *) a +sizeof(ngx_array_t) == p->d.last) { //接着销毁数组头 p->d.last = (u_char*) a; //设置内存池的last指针 } }

2.3 添加1个元素

向数组添加元素的操作有两个,ngx_array_push和ngx_array_push_n,分别添加一个和多个元素。

但实际的添加操作并不在这两个函数中完成,例如ngx_array_push返回可以在该数组数据区中添加这个元素的位置,ngx_array_push_n则返回可以在该数组数据区中添加n个元素的起始位置,而添加操作即在获得添加位置之后进行,如后文的例子。


 

点击(此处)折叠或打开

  1. void *
  2. ngx_array_push(ngx_array_t*a)
  3. {
  4.     void *elt, *new;
  5.     size_t size;
  6.     ngx_pool_t *p;

  7.     if (a->nelts ==a->nalloc) { //数组数据区满

  8.         /* the arrayis full */

  9.         size = a->size *a->nalloc; //计算数组数据区的大小

  10.         p = a->pool;

  11.         if ((u_char *)a->elts + size == p->d.last //若内存池的last指针指向数组数据区的末尾
  12.             &&p->d.last + a->size <= p->d.end) //且内存池未使用的区域可以再分配一个size大小的小空间
  13.         {
  14.             /*
  15.              * the array allocation is the lastin the pool
  16.              * and there is space for newallocation
  17.              */

  18.             p->d.last +=a->size; //分配一个size大小的小空间(a->size为数组一个元素的大小)
  19.             a->nalloc++; //实际分配小空间的个数加1

  20.         } else {
  21.             /* allocate a new array */

  22.             new =ngx_palloc(p, 2 * size); //否则,扩展数组数据区为原来的2倍
  23.             if (new == NULL) {
  24.                 return NULL;
  25.             }

  26.             ngx_memcpy(new,a->elts, size);//将原来数据区的内容拷贝到新的数据区
  27.             a->elts = new;
  28.             a->nalloc *= 2; //注意:此处转移数据后,并未释放原来的数据区,内存池将统一释放
  29.         }
  30.     }

  31.     elt = (u_char *)a->elts + a->size * a->nelts; //数据区中实际已经存放数据的子区的末尾
  32.     a->nelts++; //即最后一个数据末尾,该指针就是下一个元素开始的位置

  33.     return elt; //返回该末尾指针,即下一个元素应该存放的位置
  34. }


 

void * ngx_array_push(ngx_array_t*a) { void *elt, *new; size_t size; ngx_pool_t *p; if (a->nelts ==a->nalloc) { //数组数据区满 /* the arrayis full */ size = a->size *a->nalloc; //计算数组数据区的大小 p = a->pool; if ((u_char *)a->elts + size == p->d.last //若内存池的last指针指向数组数据区的末尾 &&p->d.last + a->size <= p->d.end) //且内存池未使用的区域可以再分配一个size大小的小空间 { /* * the array allocation is the lastin the pool * and there is space for newallocation */ p->d.last +=a->size; //分配一个size大小的小空间(a->size为数组一个元素的大小) a->nalloc++; //实际分配小空间的个数加1 } else { /* allocate a new array */ new =ngx_palloc(p, 2 * size); //否则,扩展数组数据区为原来的2倍 if (new == NULL) { return NULL; } ngx_memcpy(new,a->elts, size);//将原来数据区的内容拷贝到新的数据区 a->elts = new; a->nalloc *= 2; //注意:此处转移数据后,并未释放原来的数据区,内存池将统一释放 } } elt = (u_char *)a->elts + a->size * a->nelts; //数据区中实际已经存放数据的子区的末尾 a->nelts++; //即最后一个数据末尾,该指针就是下一个元素开始的位置 return elt; //返回该末尾指针,即下一个元素应该存放的位置 }

由此可见,向数组中添加元素实际上也是在修该内存池的last指针(若数组数据区满)及数组头信息,即使数组满了,需要扩展数据区内容,也只需要内存拷贝完成,并不需要数据的移动操作,这个效率也是相当高的。

下图是向数组中添加10个整型元素后的一个例子。代码可参考下文的例子。当然,数组元素也不仅限于例子的整型数据,也可以是其他类型的数据,如结构体等。

3. 一个例子

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

3.1代码


 

点击(此处)折叠或打开

  1. /**
  2.  * ngx_array_t test, to test ngx_array_create, ngx_array_push
  3.  */

  4. #include <stdio.h>
  5. #include "ngx_config.h"
  6. #include "ngx_conf_file.h"
  7. #include "nginx.h"
  8. #include "ngx_core.h"
  9. #include "ngx_string.h"
  10. #include "ngx_palloc.h"
  11. #include "ngx_array.h"

  12. volatile ngx_cycle_t *ngx_cycle;

  13. void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
  14.             const char *fmt, ...)
  15. {
  16. }

  17. void dump_pool(ngx_pool_t* pool)
  18. {
  19.     while (pool)
  20.     {
  21.         printf("pool = 0x%x\n", pool);
  22.         printf(" .d\n");
  23.         printf(" .last = 0x%x\n", pool->d.last);
  24.         printf(" .end = 0x%x\n", pool->d.end);
  25.         printf(" .next = 0x%x\n", pool->d.next);
  26.         printf(" .failed = %d\n", pool->d.failed);
  27.         printf(" .max = %d\n", pool->max);
  28.         printf(" .current = 0x%x\n", pool->current);
  29.         printf(" .chain = 0x%x\n", pool->chain);
  30.         printf(" .large = 0x%x\n", pool->large);
  31.         printf(" .cleanup = 0x%x\n", pool->cleanup);
  32.         printf(" .log = 0x%x\n", pool->log);
  33.         printf("available pool memory = %d\n\n", pool->d.end - pool->d.last);
  34.         pool = pool->d.next;
  35.     }
  36. }

  37. void dump_array(ngx_array_t* a)
  38. {
  39.     if (a)
  40.     {
  41.         printf("array = 0x%x\n", a);
  42.         printf(" .elts = 0x%x\n", a->elts);
  43.         printf(" .nelts = %d\n", a->nelts);
  44.         printf(" .size = %d\n", a->size);
  45.         printf(" .nalloc = %d\n", a->nalloc);
  46.         printf(" .pool = 0x%x\n", a->pool);

  47.         printf("elements: ");
  48.         int *ptr = (int*)(a->elts);
  49.         for (; ptr < (int*)(a->elts + a->nalloc * a->size); )
  50.         {
  51.             printf("0x%x ", *ptr++);
  52.         }
  53.         printf("\n");
  54.     }
  55. }

  56. int main()
  57. {
  58.     ngx_pool_t *pool;
  59.     int i;

  60.     printf("--------------------------------\n");
  61.     printf("create a new pool:\n");
  62.     printf("--------------------------------\n");
  63.     pool = ngx_create_pool(1024, NULL);
  64.     dump_pool(pool);

  65.     printf("--------------------------------\n");
  66.     printf("alloc an array from the pool:\n");
  67.     printf("--------------------------------\n");
  68.     ngx_array_t *a = ngx_array_create(pool, 10, sizeof(int));
  69.     dump_pool(pool);

  70.     for (i = 0; i < 10; i++)
  71.     {
  72.         int *ptr = ngx_array_push(a);
  73.         *ptr = i + 1;
  74.     }

  75.     dump_array(a);

  76.     ngx_array_destroy(a);
  77.     ngx_destroy_pool(pool);
  78.     return 0;
  79. }


 

3.2如何编译

请参考nginx-1.0.4源码分析—内存池结构ngx_pool_t及内存管理一文。本文编写的makefile文件如下。


 

点击(此处)折叠或打开

  1. CXX = gcc
  2. CXXFLAGS +=-g -Wall -Wextra

  3. NGX_ROOT =/usr/src/nginx-1.0.4

  4. TARGETS =ngx_array_t_test
  5. TARGETS_C_FILE= $(TARGETS).c

  6. CLEANUP = rm-f $(TARGETS) *.o

  7. all:$(TARGETS)

  8. clean:
  9. $(CLEANUP)

  10. CORE_INCS =-I. \
  11. -I$(NGX_ROOT)/src/core \
  12. -I$(NGX_ROOT)/src/event \
  13. -I$(NGX_ROOT)/src/event/modules \
  14. -I$(NGX_ROOT)/src/os/unix \
  15. -I$(NGX_ROOT)/objs \

  16. NGX_PALLOC =$(NGX_ROOT)/objs/src/core/ngx_palloc.o
  17. NGX_STRING =$(NGX_ROOT)/objs/src/core/ngx_string.o
  18. NGX_ALLOC =$(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o
  19. NGX_ARRAY =$(NGX_ROOT)/objs/src/core/ngx_array.o

  20. $(TARGETS):$(TARGETS_C_FILE)
  21. $(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING)$(NGX_ALLOC) $(NGX_ARRAY) $^ -o $@


 

3.3运行结果
该例子中内存池和数组的(内存)物理结构可参考2.3节的图。

点击(此处)折叠或打开

  1. # ./ngx_array_t_test
  2. -------------------------------- create a new pool:
  3. -------------------------------- pool = 0x860b020 .d .last = 0x860b048
  4.     .end = 0x860b420
  5.     .next = 0x0
  6.     .failed = 0 .max = 984
  7.   .current = 0x860b020
  8.   .chain = 0x0
  9.   .large = 0x0
  10.   .cleanup = 0x0
  11.   .log = 0x0 available pool memory = 984
  12. -------------------------------- alloc an array from the pool:
  13. -------------------------------- pool = 0x860b020 .d .last = 0x860b084
  14.     .end = 0x860b420
  15.     .next = 0x0
  16.     .failed = 0 .max = 984
  17.   .current = 0x860b020
  18.   .chain = 0x0
  19.   .large = 0x0
  20.   .cleanup = 0x0
  21.   .log = 0x0 available pool memory = 924
  22. array = 0x860b048 .elts = 0x860b05c
  23.   .nelts = 10
  24.   .size = 4
  25.   .nalloc = 10
  26.   .pool = 0x860b020 elements: 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa

4. 小结

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

阅读(1866) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~