Nginx学习(3)—封装的数据结构

封装的数据结构

Nginx为了做到跨平台,追求极致高效,自身定义、封装了一些数据结构。就我个人来说,无论是对这一类统一的数据结构的封装风格,还是其中的封装技巧(尽可能地少占用内存),都是非常喜欢,大赞一个。

1)整型

[cpp]  view plain copy
  1. typedef intptr_t        ngx_int_t;  
  2. typedef uintptr_t       ngx_uint_t;  
C99 标准定义了 intptr_t 和 uintptr_t 类型是给一个可以持有一个指针值的整型变量。

2)字符串型

[cpp]  view plain copy
  1. typedef struct {  
  2.     size_t      len;  // 指向字符串首字符地址  
  3.     u_char     *data; // 字符串长度  
  4. } ngx_str_t;  

可以看出,这里基本没有'\0'什么事了。这种封装的优点:1,减少计算字符串长度次数,可以直接用;2,可以重复引用一段字符串内存,长度表示结束。这样,不用对一段字符串再copy一份自己的副本,因为data指针可以指向任何字符串地址,这样可以减少不必要的内存分配与拷贝。也因为这个特性,Nginx中必须谨慎对一段字符串做出修改;另外,任何试图将ngx_str_t的data成员当作字符串来使用,都可能导致内存越界

因为不存在‘\0’,Nginx专门提供自己有关ngx_str_t的操作的API。简单笔记:

[cpp]  view plain copy
  1. // 以{...}括住,只适用于字符串赋值初始化,计算str长度为sizeof关键字,因此str为字符串常量!         
  2. #define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }  
  3. #define ngx_null_string     { 0, NULL }  
  4.   
  5. // 字符串赋值,text为字符串常量!因为是两行代码,在if/for/while等语句中单独使用需要用花括号括起来!  
  6. #define ngx_str_set(str, text)                                               \  
  7.     (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text  
  8. #define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL     

另外Nginx简单封装了glibc的字符串操作API,具体见ngx_string.h。   

3)链表容器

[cpp]  view plain copy
  1. /* 描述链表的一个结点,此节点实际上是一个数组,拥有一段连续内存 */  
  2. typedef struct ngx_list_part_s  ngx_list_part_t;  
  3. struct ngx_list_part_s {  
  4.     void             *elts;   // 指向数组起始地址   
  5.     ngx_uint_t        nelts;  // 表示数组中已经使用多少个元素,其值 <= ngx_list_t中的nalloc  
  6.     ngx_list_part_t  *next;   // 下一个链表节点  
  7. };   
  8.   
  9. /* 描述整个链表 */  
  10. typedef struct {  
  11.     ngx_list_part_t  *last;   // 链表尾节点  
  12.     ngx_list_part_t   part;   // 链表首节点  
  13.     size_t            size;   // 链表的每一个节点实质上是一个数组,但数组元素的类型是不确定的,由size表示每一个数组元素能够存储的最大值  
  14.     ngx_uint_t        nalloc; // 表示每一个ngx_list_part_t节点的容量  
  15.     ngx_pool_t       *pool;   // 链表中管理内存分配的内存池对象  
  16. } ngx_list_t;    

给出书中简图,一种可能的ngx_list_t链表的内存分布结构。

Nginx学习(3)—封装的数据结构_第1张图片

对于链表,Nginx也提供了一组接口:

ngx_list_create接口用于创建新的链表;

ngx_list_init接口初始化一个已有链表;

ngx_list_push接口用于添加新元素。具体情况详见

4)动态数组容器

[cpp]  view plain copy
  1. typedef struct {  
  2.     void        *elts;   // 指向数组起始地址  
  3.     ngx_uint_t   nelts;  // 表示数组中已经使用多少个元素,其值 <= ngx_list_t中的nalloc  
  4.     size_t       size;   // 一个数组元素能够存储的最大值  
  5.     ngx_uint_t   nalloc; // 当nelts增长到达nalloc 时,如果再往此数组中存储元素,则会引发数组的扩容。  
  6.                          // 数组的容量将会扩展到原有容量的2倍大小。实际上是分配新的一块内存,  
  7.                          // 新的一块内存的大小是原有内存大小的2倍。原有的数据会被拷贝到新的一块内存中。  
  8.     ngx_pool_t  *pool;   // 管理内存分配的内存池对象  
  9. } ngx_array_t;  

基本上跟链表容器的节点元素一致,接口也类似,具体详情见

ngx_array_t容器具备以下三个优点:

  1. 访问速度快;
  2. 允许元素个数具备不确定性;
  3. 负责元素占用内存的分配,这些内存将由内存池统一管理;
画上书中图描述下此结构体及使用方法:
Nginx学习(3)—封装的数据结构_第2张图片

5)缓冲区

缓冲区ngx_buf_t是Nginx处理大数据的关键数据结构,它既应用于内存数据也应用于磁盘数据。

[cpp]  view plain copy
  1. typedef struct ngx_buf_s  ngx_buf_t;  
  2. typedef void *            ngx_buf_tag_t;  
  3. struct ngx_buf_s {  
  4.     /* 表示从此为止开始处理内存数据,同一个ngx_buf_t可能被多次反复处理 */  
  5.     u_char          *pos;  
  6.       
  7.     /* Nginx有效处理的内存结束地址 */  
  8.     u_char          *last;  
  9.       
  10.     /* 处理文件时,含义与pos、last相同 */  
  11.     off_t            file_pos;  
  12.     off_t            file_last;  
  13.   
  14.     /* 如果ngx_buf_t缓冲区用于内存,这个则指向buf起始地址与尾地址...那上面两字段毛意思,混乱中... */  
  15.     u_char          *start;         /* start of buffer */  
  16.     u_char          *end;           /* end of buffer */  
  17.       
  18.     /* 表示当前缓冲区类型 */  
  19.     ngx_buf_tag_t    tag;  
  20.       
  21.     /* 引用的文件 */  
  22.     ngx_file_t      *file;  
  23.       
  24.     /* 当前缓冲区的影子缓冲区(笔记:很少用到,不建议使用) */  
  25.     ngx_buf_t       *shadow;  
  26.   
  27.     /* the buf's content could be changed */  
  28.     /* 源码中有英文注释,翻译为中文...  
  29.      * 以下均为标志位,且其值置1的意义 
  30.      */  
  31.     /* 临时内存标志位,表示数据在内存中,可修改 */  
  32.     unsigned         temporary:1;  
  33.   
  34.     /* 
  35.      * the buf's content is in a memory cache or in a read only memory 
  36.      * and must not be changed 
  37.      */  
  38.     /* 表示数据在内存中,且不能被修改 */  
  39.     unsigned         memory:1;  
  40.   
  41.     /* the buf's content is mmap()ed and must not be changed */  
  42.     /* 表示内存是用mmap系统调用映射过来,不可被修改 */  
  43.     unsigned         mmap:1;  
  44.   
  45.     /* 表示可回收 */  
  46.     unsigned         recycled:1;  
  47.       
  48.     /* buf缓冲区处理的是文件,不是内存 */  
  49.     unsigned         in_file:1;  
  50.       
  51.     /* 需要执行flush操作 */  
  52.     unsigned         flush:1;  
  53.       
  54.     /* 使用同步方式(可能阻塞Nginx进程,谨慎使用) */  
  55.     unsigned         sync:1;  
  56.       
  57.     /* ngx_buf_t可由ngx_chain_t串联,此标志位表示当前是最后一块待处理的缓冲区 */  
  58.     unsigned         last_buf:1;  
  59.       
  60.     /* 表示是ngx_chain_t中最后一块缓冲区 */  
  61.     unsigned         last_in_chain:1;  
  62.   
  63.     /* 配合影子缓冲区使用,表示最后一个影子缓冲区 */  
  64.     unsigned         last_shadow:1;  
  65.       
  66.     /* 表示当亲缓冲区属于临时文件 */  
  67.     unsigned         temp_file:1;  
  68.   
  69.     /* STUB */ int   num;  
  70. };  
ngx_buf_t是一种基本的数据结构,本质上提供的仅仅是一些指针成员和标志位。如果我们自定义一个ngx_buf_t结构,则应该根据业务需求自行定义。

5)缓冲区链表

[cpp]  view plain copy
  1. typedef struct ngx_chain_s       ngx_chain_t;      
  2. struct ngx_chain_s {                               
  3.     ngx_buf_t    *buf;  // 指向当前缓冲区  
  4.     ngx_chain_t  *next; // 指向下一个,如果是最后一个ngx_chain_t,必须置NULL  
  5. };  

这个是与ngx_buf_t配合使用的链表结构。摘下博文《Nginx模块开发入门》简图:

Nginx学习(3)—封装的数据结构_第3张图片

你可能感兴趣的:(nginx)