Nginx学习笔记(三):封装的数据结构
2014-01-05 11:18
375 查看
前言
不能保证每天都有时间学这个,毕竟其他方面还有很多的事情需要做,但一定会利用好时间,充实自己。另外,大概扫了下Nginx自己封装的数据结构类型,确实还是蛮多的。所以,暂时打算学一点,更新一点。这一篇也主要是为自己开发一个简单HTTP模块做准备。封装的数据结构
Nginx为了做到跨平台,追求极致高效,自身定义、封装了一些数据结构。就我个人来说,无论是对这一类统一的数据结构的封装风格,还是其中的封装技巧(尽可能地少占用内存),都是非常喜欢,大赞一个。1)整型 <ngx_config.h>
typedef intptr_t ngx_int_t; typedef uintptr_t ngx_uint_t;C99 标准定义了 intptr_t 和 uintptr_t 类型是给一个可以持有一个指针值的整型变量。
2)字符串型 <ngx_string.h>
typedef struct { size_t len; // 指向字符串首字符地址 u_char *data; // 字符串长度 } ngx_str_t;
可以看出,这里基本没有'\0'什么事了。这种封装的优点:1,减少计算字符串长度次数,可以直接用;2,可以重复引用一段字符串内存,长度表示结束。这样,不用对一段字符串再copy一份自己的副本,因为data指针可以指向任何字符串地址,这样可以减少不必要的内存分配与拷贝。也因为这个特性,Nginx中必须谨慎对一段字符串做出修改;另外,任何试图将ngx_str_t的data成员当作字符串来使用,都可能导致内存越界!
因为不存在‘\0’,Nginx专门提供自己有关ngx_str_t的操作的API。简单笔记:
// 以{...}括住,只适用于字符串赋值初始化,计算str长度为sizeof关键字,因此str为字符串常量! #define ngx_string(str) { sizeof(str) - 1, (u_char *) str } #define ngx_null_string { 0, NULL } // 字符串赋值,text为字符串常量!因为是两行代码,在if/for/while等语句中单独使用需要用花括号括起来! #define ngx_str_set(str, text) \ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text #define ngx_str_null(str) (str)->len = 0; (str)->data = NULL
另外Nginx简单封装了glibc的字符串操作API,具体见ngx_string.h。
3)链表容器 <ngx_list.h>
/* 描述链表的一个结点,此节点实际上是一个数组,拥有一段连续内存 */ typedef struct ngx_list_part_s ngx_list_part_t; struct ngx_list_part_s { void *elts; // 指向数组起始地址 ngx_uint_t nelts; // 表示数组中已经使用多少个元素,其值 <= ngx_list_t中的nalloc ngx_list_part_t *next; // 下一个链表节点 }; /* 描述整个链表 */ typedef struct { ngx_list_part_t *last; // 链表尾节点 ngx_list_part_t part; // 链表首节点 size_t size; // 链表的每一个节点实质上是一个数组,但数组元素的类型是不确定的,由size表示每一个数组元素能够存储的最大值 ngx_uint_t nalloc; // 表示每一个ngx_list_part_t节点的容量 ngx_pool_t *pool; // 链表中管理内存分配的内存池对象 } ngx_list_t;
给出书中简图,一种可能的ngx_list_t链表的内存分布结构。
对于链表,Nginx也提供了一组接口:
ngx_list_create接口用于创建新的链表;
ngx_list_init接口初始化一个已有链表;
ngx_list_push接口用于添加新元素。具体情况详见<ngx_list.c>。
4)动态数组容器 <ngx_array.h>
typedef struct { void *elts; // 指向数组起始地址 ngx_uint_t nelts; // 表示数组中已经使用多少个元素,其值 <= ngx_list_t中的nalloc size_t size; // 一个数组元素能够存储的最大值 ngx_uint_t nalloc; // 当nelts增长到达nalloc 时,如果再往此数组中存储元素,则会引发数组的扩容。 // 数组的容量将会扩展到原有容量的2倍大小。实际上是分配新的一块内存, // 新的一块内存的大小是原有内存大小的2倍。原有的数据会被拷贝到新的一块内存中。 ngx_pool_t *pool; // 管理内存分配的内存池对象 } ngx_array_t;
基本上跟链表容器的节点元素一致,接口也类似,具体详情见<ngx_array.c>。
ngx_array_t容器具备以下三个优点:
访问速度快;
允许元素个数具备不确定性;
负责元素占用内存的分配,这些内存将由内存池统一管理;
画上书中图描述下此结构体及使用方法:
5)缓冲区 <ngx_buf.h>
缓冲区ngx_buf_t是Nginx处理大数据的关键数据结构,它既应用于内存数据也应用于磁盘数据。typedef struct ngx_buf_s ngx_buf_t; typedef void * ngx_buf_tag_t; struct ngx_buf_s { /* 表示从此为止开始处理内存数据,同一个ngx_buf_t可能被多次反复处理 */ u_char *pos; /* Nginx有效处理的内存结束地址 */ u_char *last; /* 处理文件时,含义与pos、last相同 */ off_t file_pos; off_t file_last; /* 如果ngx_buf_t缓冲区用于内存,这个则指向buf起始地址与尾地址...那上面两字段毛意思,混乱中... */ u_char *start; /* start of buffer */ u_char *end; /* end of buffer */ /* 表示当前缓冲区类型 */ ngx_buf_tag_t tag; /* 引用的文件 */ ngx_file_t *file; /* 当前缓冲区的影子缓冲区(笔记:很少用到,不建议使用) */ ngx_buf_t *shadow; /* the buf's content could be changed */ /* 源码中有英文注释,翻译为中文... * 以下均为标志位,且其值置1的意义 */ /* 临时内存标志位,表示数据在内存中,可修改 */ unsigned temporary:1; /* * the buf's content is in a memory cache or in a read only memory * and must not be changed */ /* 表示数据在内存中,且不能被修改 */ unsigned memory:1; /* the buf's content is mmap()ed and must not be changed */ /* 表示内存是用mmap系统调用映射过来,不可被修改 */ unsigned mmap:1; /* 表示可回收 */ unsigned recycled:1; /* buf缓冲区处理的是文件,不是内存 */ unsigned in_file:1; /* 需要执行flush操作 */ unsigned flush:1; /* 使用同步方式(可能阻塞Nginx进程,谨慎使用) */ unsigned sync:1; /* ngx_buf_t可由ngx_chain_t串联,此标志位表示当前是最后一块待处理的缓冲区 */ unsigned last_buf:1; /* 表示是ngx_chain_t中最后一块缓冲区 */ unsigned last_in_chain:1; /* 配合影子缓冲区使用,表示最后一个影子缓冲区 */ unsigned last_shadow:1; /* 表示当亲缓冲区属于临时文件 */ unsigned temp_file:1; /* STUB */ int num; };ngx_buf_t是一种基本的数据结构,本质上提供的仅仅是一些指针成员和标志位。如果我们自定义一个ngx_buf_t结构,则应该根据业务需求自行定义。
5)缓冲区链表 <ngx_buf.h>
typedef struct ngx_chain_s ngx_chain_t; <ngx_core.h> struct ngx_chain_s { <ngx_buf.h> ngx_buf_t *buf; // 指向当前缓冲区 ngx_chain_t *next; // 指向下一个,如果是最后一个ngx_chain_t,必须置NULL };
这个是与ngx_buf_t配合使用的链表结构。摘下博文《Nginx模块开发入门》简图:
总结
暂时没法总结。。还没体会这些数据的好处。对那个str_list_t的设计,比较喜欢。主要参考
《深入理解Nginx》《Nginx从入门到精通》
《Nginx模块开发入门》
相关文章推荐
- 数据结构封装之《LinkQueue2.0改进链式队列》
- 3D游戏引擎底层数据结构的封装之List
- 3D游戏引擎底层数据结构的封装之Stack
- 五种数据结构的单元test---需复合封装的JedisUtil使用
- 数据结构4.进一步封装的双向链表
- 【转载】C++与C#对常用数据结构封装的对比(STL vs System.Collections.Generic)
- 数据结构封装之 《SeqList顺序表》
- 封装图这一种数据结构
- 3D游戏引擎底层数据结构的封装之List
- Nginx学习笔记(三) Nginx基本数据结构
- 数据结构和二叉树操作的封装
- 封装树形数据结构
- 数据结构封装之《LinkList单向链表》
- non object数据结构对象封装和序列化
- java 中的JDK封装的数据结构和算法解析(集合类)----链表 List 之 LinkedList
- 数据结构封装之《CircleList循环链表》
- 封装TableView有可能用到的数据结构和UITableViewCell的一个继承类
- 【算法和数据结构】线性表(二)队列的定义和封装
- JS高级-数据结构的封装
- 数据结构中的栈,在解决很多问题都有用处,比如括号匹配,迷宫求解,表达式求值等等 java中有封装好的类,可以直接调用。