nginx源码初读(2)--让烦恼从数据结构开始(ngx_buf/ngx_chain)
2016-02-19 19:00
375 查看
chain和buf是nginx过滤模块涉及到的结构体,而pool则是管理内存分配的一个结构。在日常的过滤模块中,这两类结构使用非常频繁,所以nginx采用类似freelist重复利用的原则,将使用完毕的chain或者buf结构体,放置到一个固定的空闲链表里,以待下次使用。
比如,在通用内存池结构体中,pool->chain变量里面就保存着释放的chain。而一般的buf结构体,没有模块间公用的空闲链表池,都是保存在各模块的缓存空闲链表池里面。对于buf结构体,还有一种busy链表,表示该链表中的buf都处于输出状态,如果buf输出完毕,这些buf就可以释放并重复利用了。
再比如,nginx的过滤模块在处理从别的filter模块或者是handler模块传递过来的数据(实际上就是需要发送给客户端的http response)。这个传递过来的数据是以一个链表的形式(ngx_chain_t)。而且数据可能被分多次传递过来。也就是多次调用filter的处理函数,以不同的ngx_chain_t。如果chain最后一个节点的next值没有被设为NULL,数据就无法完成接收。
接下来我们就看看chain和buf的结构体,最后再研究一下pool。
第一点:ngx_buf_t
ngx_buf_t是Nginx处理大数据的关键数据结构,是一种抽象的数据结构,它代表某种具体的数据,既应用于内存数据(缓冲区)也应用于磁盘数据(文件)或者一些元数据(指示链表读取者对链表的处理方式)。
下面我们来看看它的定义:
对于此对象的创建,可以直接在某个ngx_pool_t上分配,然后根据需要,给对应的字段赋值。也可以使用定义好的2个宏,这两个宏使用类似函数:
对于创建temporary字段为1的buf(就是其内容可以被后续的filter模块进行修改),可以直接使用函数ngx_create_temp_buf进行创建:
为了配合对ngx_buf_t的使用,nginx定义了以下的宏方便操作:
第二点:ngx_chain_t
ngx_chain_t:
创建缓冲区链表的函数:
该宏释放一个ngx_chain_t类型的对象:
如果要释放整个chain,则迭代此链表,对每个节点使用此宏即可。注意:对ngx_chaint_t类型的释放,并不是真的释放了内存,而仅仅是把这个对象挂在了这个pool对象的一个叫做chain的字段对应的chain上,以供下次从这个pool上分配ngx_chain_t类型对象的时候,快速的从这个pool->chain上取下链首元素就返回了,当然,如果这个链是空的,才会真的在这个pool上使用ngx_palloc函数进行分配。
比如,在通用内存池结构体中,pool->chain变量里面就保存着释放的chain。而一般的buf结构体,没有模块间公用的空闲链表池,都是保存在各模块的缓存空闲链表池里面。对于buf结构体,还有一种busy链表,表示该链表中的buf都处于输出状态,如果buf输出完毕,这些buf就可以释放并重复利用了。
再比如,nginx的过滤模块在处理从别的filter模块或者是handler模块传递过来的数据(实际上就是需要发送给客户端的http response)。这个传递过来的数据是以一个链表的形式(ngx_chain_t)。而且数据可能被分多次传递过来。也就是多次调用filter的处理函数,以不同的ngx_chain_t。如果chain最后一个节点的next值没有被设为NULL,数据就无法完成接收。
接下来我们就看看chain和buf的结构体,最后再研究一下pool。
第一点:ngx_buf_t
ngx_buf_t是Nginx处理大数据的关键数据结构,是一种抽象的数据结构,它代表某种具体的数据,既应用于内存数据(缓冲区)也应用于磁盘数据(文件)或者一些元数据(指示链表读取者对链表的处理方式)。
下面我们来看看它的定义:
typedef struct ngx_buf_s ngx_buf_t; typedef void * ngx_buf_tag_t; struct ngx_buf_s { u_char *pos; /* 本次内存数据处理的起始位置,因为一个buf可能被多次处理。 * if (ngx_buf_in_memory(in->buf)) { in->buf->pos += (size_t) sent; } */ u_char *last; /* pos和last之间即为nginx本次想要处理的区域。 * if (ngx_buf_in_memory(in->buf)) { in->buf->pos = in->buf->last; } */ off_t file_pos; off_t file_last; /* 处理文件时,概念与内存相同,表示相对于文件头的偏移量。 * size = cl->buf->file_last - cl->buf->file_pos; */ u_char *start; /* start of buffer */ u_char *end; /* end of buffer */ /* 当buf所指向的数据在内存里的时候,这一整块内存包含的内容可能被包含在多个buf中, * 比如在某段数据中间插入了其他的数据,这一块数据就需要被拆分开。 * 那么这些buf中的start和end都指向这一块内存的开始地址和结束地址。 * 而pos和last指向本buf所实际包含的数据的开始和结尾。 */ ngx_buf_tag_t tag; /* 见上定义,表示当前缓冲区的类型,例如由哪个模块使用就指向这个模块ngx_module_t变量的地址。 * buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module; */ ngx_file_t *file; /* 指向文件的引用。之后介绍这个结构体。*/ ngx_buf_t *shadow; /* 当前缓冲区的影子缓冲区,该成员很少用到。当缓冲区转发上游服务器的响应时才使用了shadow成员, * 这是因为nginx太节约内存了,分配一块内存并使用ngx_buf_t表示接收到的上游服务器响应后, * 在向下游客户端转发时可能会把这块内存存储到文件中,也可能直接向下游发送,此时nginx绝对不会 * 重新复制一份内存用于新的目的,而是再次建立一个ngx_buf_t结构体指向原内存,这样多个ngx_buf_t * 结构体指向了同一份内存,它们之间的关系就通过shadow成员来引用,一般不建议使用。 * buf = cl->buf->shadow; */ unsigned temporary:1; /* 表示buf包含的内容在内存缓冲区中 c97a ,在被处理过程中可以修改,不会造成问题。 * if (b->pos == dst) { b->sync = 1; b->temporary = 0; } */ unsigned memory:1; /* 与temporary相反,表示不可以被修改 */ unsigned mmap:1; /* 为1时表示该buf是通过mmap使用内存映射从文件中映射到内存中的,不可以被修改 */ unsigned recycled:1; /* 可以回收的。也就是这个buf是可以被释放的。这个字段通常是配合shadow字段一起使用的, 对于使用ngx_create_temp_buf 函数创建的buf,并且是另外一个buf的shadow, 那么可以使用这个字段来标示这个buf是可以被释放的。*/ unsigned in_file:1; /* 为1时表示该buf所包含的内容是在文件中。*/ unsigned flush:1; /* 为1时表示需要执行flush操作,遇到有flush字段被设置为1的的buf的chain, 则该chain的数据即便不是最后结束的数据(last_buf被设置,标志所有要输出的内容都完了), 也会进行输出,不会受postpone_output配置的限制,但是会受到发送速率等其他条件的限制。*/ unsigned sync:1; /* 标志位,对于操作这块缓冲区时是否使用同步方式,需谨慎考虑,这可能会阻塞nginx进程, nginx中所有操作几乎都是异步的,这是它支持高并发的关键。 有些框架代码在sync为1时可能会有阻塞的方式进行I/O操作,它的意义视使用它的nginx模块而定。*/ unsigned last_buf:1; /* 数据被以多个chain传递给了过滤器,此字段为1表明这是最后一个buf。 * if (in->buf->last_buf) last = 1; */ unsigned last_in_chain:1; /* 在当前的chain里面,此buf是最后一个。特别要注意的是last_in_chain的buf不一定是last_buf, 但是last_buf的buf一定是last_in_chain的。这是因为数据会被以多个chain传递给某个filter模块。 * if (in->last_in_chain) { if (bsize < (off_t) size) { size = (size_t) bsize; recycled = 0; } // else if ... } */ unsigned last_shadow:1; /* 在创建一个buf的shadow的时候,通常将新创建的一个buf的last_shadow置为1,表示为最后一个shadow。 * if (cl->buf->last_shadow) { if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) { return NGX_ABORT; } cl->buf->last_shadow = 0; } */ unsigned temp_file:1; /* 由于内存使用限制,有时候一些buf需要被写到磁盘上的临时文件中去,那么就设置此标志表示为临时文件。*/ /* STUB */ int num; };
对于此对象的创建,可以直接在某个ngx_pool_t上分配,然后根据需要,给对应的字段赋值。也可以使用定义好的2个宏,这两个宏使用类似函数:
#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t)) #define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))
对于创建temporary字段为1的buf(就是其内容可以被后续的filter模块进行修改),可以直接使用函数ngx_create_temp_buf进行创建:
ngx_buf_t * ngx_create_temp_buf(ngx_pool_t *pool, size_t size) { ngx_buf_t *b = ngx_calloc_buf(pool); if (b == NULL) return NULL; // 从pool中分配出size大小的内存给buf b->start = ngx_palloc(pool, size); // 指向内存开始的地方 if (b->start == NULL) return NULL; // set by ngx_calloc_buf(): // b->file_pos = b->file_last = b->tag = 0; // b->file = b->shadow = NULL; // and flags b->pos = b->start; // 指向内存块的起始位置便于写入 b->last = b->start; b->end = b->last + size; // 指向内存结束的地方 b->temporary = 1; return b; }
为了配合对ngx_buf_t的使用,nginx定义了以下的宏方便操作:
// buf是否在内存里 #define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap) // buf里面的内容是否仅仅在内存里,并且没有在文件里 #define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file) // buf是否是一个特殊的buf,只含有特殊的标志和没有包含真正的数据 #define ngx_buf_special(b) \ ((b->flush || b->last_buf || b->sync) \ && !ngx_buf_in_memory(b) && !b->in_file) // buf是否是一个只包含sync标志而不包含真正数据的特殊buf #define ngx_buf_sync_only(b) \ (b->sync \ && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf) // 返回该buf所含数据的大小,不管这个数据是在文件里还是在内存里。 #define ngx_buf_size(b) \ (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \ (b->file_last - b->file_pos))
第二点:ngx_chain_t
ngx_chain_t:
typedef struct ngx_chain_s ngx_chain_t; struct ngx_chain_s { ngx_buf_t *buf; // 指向当前的buf缓冲区 ngx_chain_t *next; // 如果这是最后一个ngx_chain_t,需要把next置为NULL };
创建缓冲区链表的函数:
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool) { ngx_chain_t *cl; cl = pool->chain; if (cl) { pool->chain = cl->next; return cl; } cl = ngx_palloc(pool, sizeof(ngx_chain_t)); if (cl == NULL) { return NULL; } return cl; }
该宏释放一个ngx_chain_t类型的对象:
#define ngx_free_chain(pool, cl) \ cl->next = pool->chain; \ pool->chain = cl
如果要释放整个chain,则迭代此链表,对每个节点使用此宏即可。注意:对ngx_chaint_t类型的释放,并不是真的释放了内存,而仅仅是把这个对象挂在了这个pool对象的一个叫做chain的字段对应的chain上,以供下次从这个pool上分配ngx_chain_t类型对象的时候,快速的从这个pool->chain上取下链首元素就返回了,当然,如果这个链是空的,才会真的在这个pool上使用ngx_palloc函数进行分配。
相关文章推荐
- 数据结构与算法——图
- poj 1451(Trie)
- iOS学习_Lesson01_数据结构
- 关于VFS文件系统中的superblock、inode、d_entry和file数据结构
- Redis中5种数据结构的使用场景介绍
- Redis中5种数据结构的使用场景介绍
- 《数据结构与算法分析(c 描述)》—— 第二章笔记
- 数据结构之栈(四)
- HDU 5479 Scaena Felix(简单的数据结构题目)
- 《数据结构与算法分析(c 描述)》—— 第一章笔记
- c++ 数据结构 哈夫曼树
- poj 1195(二维树状数组)
- 数据结构实验之二叉树三:统计叶子数
- 学习笔记------数据结构(C语言版)数组之十字链表
- 数据结构---树形结构
- [NOIP2004]合并果子 T2 数据结构 简单贪心
- 数据结构之栈(三)
- C#中使用Redis不同数据结构的内存占有量的疑问和对比测试
- Java数据结构----优先队列
- 数据结构