导语:通过网上阅读查阅,尽量把nginx源码这一部分整理完善,让以后想要学习nginx的同学也可以快速上手。这一节涉及src/core/ngx_buf.h|c的数据结构,其次,看一些大牛文章时候 get 了一个好的画图工具- graphviz 和一个拍照app-扫描全能王,可以帮助理清源码的思路。
一、结构体、相关宏定义、函数声明梳理
分析采用nginx-1.6.2源码。在src/core/ngx_buf.h|c中的ngx_buf_t 结构体还是很重要,看源码的过程中遇到几次,确实有必要好好理理。ngx_buf_t 包含于ngx_chain_t 结构体中。
// 这个就是链表的形式,还是把ngx_buf_t串起来,
// 主要作用就是接受nginx服务器的http请求包头、包体、以及响应客户端的应答包头、包体
// 都会放在chain链表缓冲区
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
接着是ngx_buf_t结构体,ngx_buf_t 就是ngx_chain_t链表每个节点的实际数据,缓冲区ngx_buf_t是nginx处理大数据的关键数据结构,它既可以应用于内存也可以用于磁盘数据注释。里面的参数设置,考虑的情况真多,而且最后标志位都只占1位,可以说对内存利用锱铢必较,值得借鉴学习。
typedef void * ngx_buf_tag_t;
typedef struct ngx_buf_s ngx_buf_t;
// ngx_buf_t 就是ngx_chain_t链表每个节点的实际数据,
// 缓冲区ngx_buf_t是nginx处理大数据的关键数据结构,它既可以应用于内存也可以用于磁盘数据
struct ngx_buf_s {
// pos指向的是这段数据在内存中的开始位置
u_char *pos;
// last指向的是这段数据在内存中的结束位置
u_char *last;
// file_pos指向这段数据的开始位置在文件中的偏移量
off_t file_pos;
// file_last指向这段数据的结束位置在文件中的偏移量
off_t file_last;
// 如果ngx_buf_t 缓冲区用于内存,那么start指向这段内存的起始地址
u_char *start; /* start of buffer */
// 如果ngx_buf_t 缓冲区用于内存,那么end指向这段内存的结束地址
u_char *end; /* end of buffer */
// 实际上就是一个void *类型的指针。使用者可以关联任何对象上去
// 由哪个模块使用就执行哪个模块的ngx_module_t结构
ngx_buf_tag_t tag;
// 当buf所包含的内容在文件中时,file字段指向对应的文件对象上去
ngx_file_t *file;
// 当这个buf完整copy了另外一个buf的所有字段的时候,那么这个两个buf指向的实际上是同一块内存,或者是
// 同一个文件的同一部分,此时这两个buf的shadow字段指向对方的。释放的时候特别注意。
ngx_buf_t *shadow;
// 为1表示该buf所包含的内容是一个用户创建的内存块中,并且可以被在filter处理的过程中进行变更,
// 不会造成问题
unsigned temporary:1;
/*the buf's content is in a memory cache or in a read only memory
* and must not be changed
*
* 为1表示该buf所包含的内容是在内存中,但是这些内容却不能被进行处理的filter进行变更
*/
unsigned memory:1;
/* the buf's content is mmap()ed and must not be changed
* 为1表示该buf所包含的内容是在内存中,是通过mmap使用内存映射从文件中映射到内存中的,
* 这些内容不能进行变更
*/
unsigned mmap:1;
// 可以回收,也就是这个buf是可以释放的
unsigned recycled:1;
// 为1时表示该buf所包含的内容是在文件中
unsigned in_file:1;
// 遇到有flush字段被设置为1的buf的chain,则该chain的数据即便不是最后结束的数据,也会输出 ??
unsigned flush:1;
/* 对于操作这块缓冲区是否使用同步方式,需要谨慎考虑,这可能会阻塞nginx进程,nginx中所有的操作
* 几乎都是异步的,这是它支持高并发的关键。
*/
unsigned sync:1;
// 数据被多个chain传递给过滤器,此字段为1表明这是最后一个buf
unsigned last_buf:1;
// 在当前的chain里面,此buf是最后一个。
unsigned last_in_chain:1;
// 在创建一个buf的shadow的时候,通常将新创建的一个buf的last_shadow置为1
unsigned last_shadow:1;
// 由于内存使用的限制,有时候一些buf的内容需要被写到磁盘上的临时文件中去,这个时候就设置次标志
unsigned temp_file:1;
// 这个参数还没有弄清楚 ????
/* STUB */ int num;
};
来一张手绘图清晰明了,这个就是用扫描全能王app拍的,我画的太丑了,能理解清除意思就可以
ngx_bufs_t结构体
// ngx_bufs_t起到了一个管理作用,用于说明当前使用的bufs的数量和每个buf的存储空间
// 在ngx_create_chain_of_bufs可以看到怎么使用
typedef struct {
ngx_int_t num;
size_t size;
} ngx_bufs_t;
宏定义和部分函数声明解释,一些函数涉及body filter(包体过滤)http的东西先不管,主要说数据结构。nginx代码看起来就很舒服,很多数据类型,宏定义,哪怕一个很简单的操作都要用nginx的标准封装,也是值得学习的地方
#define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR
// 返回这个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))
// 对于创建temporary字段为1的buf(内容可以被后续的filter模块进行修改),直接用ngx_create_temp_buf个函数创建
// 其中pool分配该buf和buf使用的内存,size是该buf使用的内存大小
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);
#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t))
#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))
// 该函数创建一个ngx_chain_t对象,并返回指向对象的指针,失败返回NULL
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);
// 该函数释放一个ngx_chain_t的对象,如果要释放整个chain,则迭代此链表
// 对ngx_chain_t的释放,并不是真正的释放,而是把这个对象挂在pool对象一个叫chain的
// 字段对应的chain上,以供下次可以快速取到
#define ngx_free_chain(pool, cl) \
cl->next = pool->chain; \
pool->chain = cl
二、函数的解释 , 函数在/src/core/ngx_buf.c文件中。里面的函数都比较简单,说的彻底一点,还是指针指来指去
#include <ngx_config.h>
#include <ngx_core.h>
// 用ngx_create_temp_buf函数在内存池上创建一个临时缓冲区
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
b = ngx_calloc_buf(pool); // 从内存池pool中创建一个ngx_buf_t的大小的内存,在ngx_buf.h中宏定义了
if (b == NULL) {
return NULL;
}
b->start = ngx_palloc(pool, size);// 开辟缓冲区空间
if (b->start == NULL) {
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*/
// 起始位置pos和start
b->pos = b->start;
b->last = b->start;
b->end = b->last + size; // end为缓冲区末尾
b->temporary = 1; // 临时缓冲区,内存可以被修改
return b;
}
// 创建缓冲区链表,如果有直接拿,么有从pool请求一段空间
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool)
{
ngx_chain_t *cl;
cl = pool->chain;
if (cl) {
//因为c1将会被使用,所以指向c1的下一个
pool->chain = cl->next;
return cl;
}
cl = ngx_palloc(pool, sizeof(ngx_chain_t));
if (cl == NULL) {
return NULL;
}
return cl;
}
// ngx_create_temp_buf只是创建了单个缓冲区空间。如果要创建一片连续的缓冲区,
// 就由链表统一管理,则需要使用ngx_create_chain_of_bus函数
// 从内存池pool中创建一个bufs->num长度的buf->size大小的链表,返回ngx_chain_t链表头节点
ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)
{
u_char *p;
ngx_int_t i;
ngx_buf_t *b;
ngx_chain_t *chain, *cl, **ll;
p = ngx_palloc(pool, bufs->num * bufs->size); // 上面描述了
if (p == NULL) {
return NULL;
}
// 这里用一个二级指针保存第一个节点指针地址,同样也保存了后面节点指针地址,这么做
// 少了一次循环内的判断,否则我们需要判断出第一次循环,并给chain赋值
ll = &chain;
for (i = 0; i < bufs->num; i++) {
b = ngx_calloc_buf(pool); // 在内存池中创建ngx_buf_t对象,在ngx_buf.h头文件宏定义了
if (b == NULL) {
return NULL;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*
*/
// 初始化每个buf缓冲区的起始位置
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
// 此时p会指向下一个缓冲区
p += bufs->size;
b->end = p;
// 创建一个ngx_chain_t节点
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
// 将ngx_chain_t与buf关联起来
cl->buf = b;
// ll保存的上一个节点指针的指针,赋值相当于给上一个节点next指针赋值
*ll = cl;
// 取本节点next指针地址,用于下次循环使用
ll = &cl->next;
}
*ll = NULL;
// 返回ngx_chain_t链表的头节点
return chain;
}
// 将其他缓冲区拷贝到已有缓冲区末尾,也就是in链表插入到chain链表末尾
ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
{
ngx_chain_t *cl, **ll;
ll = chain;
// 找到chain链表的结尾,然后给ll
for (cl = *chain; cl; cl = cl->next) {
ll = &cl->next;
}
// 遍历in,并且创建分配chain的基本节点,并将其buf指向in的部分
while (in) {
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = in->buf; // 开始复制
*ll = cl; // 将next指向c1
ll = &cl->next; // 将ll指针移向c1的next
in = in->next; // in向下移动
}
*ll = NULL; // 最后一个节点指向NULL
return NGX_OK;
}
// 获取一个可用的ngx_chain_t节点,上面需要有一个可用的ngx_buf_t
ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
{
ngx_chain_t *cl;
// 若有空闲链表中有节点,直接返回
if (*free) {
cl = *free;
*free = cl->next; // 不太明白为什么再移动指针
cl->next = NULL;
return cl;
}
// 程序移动到这里表示free是个空指针,需要申请空间
cl = ngx_alloc_chain_link(p);
if (cl == NULL) {
return NULL;
}
cl->buf = ngx_calloc_buf(p); // 申请新的buf空间
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
return cl;
}
// ngx_chain_update_chains函数会将busy链表中的空闲节点回收到free链表中
// 将out链表插入到busy链表尾部,同时将合并后的链表从头开始的所有没有使用的节点,插入到空闲链表
// 合并链表后,out为NULL
void
ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,
ngx_chain_t **out, ngx_buf_tag_t tag)
{
ngx_chain_t *cl;
if (*busy == NULL) {
*busy = *out;
} else {
for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
cl->next = *out;
}
*out = NULL;
while (*busy) {
cl = *busy;
// 这个节点内存占有,不满足条件,直接退出
if (ngx_buf_size(cl->buf) != 0) {
break;
}
// 缓冲区类型不为void *,直接释放,在循环开始
if (cl->buf->tag != tag) {
*busy = cl->next;
ngx_free_chain(p, cl);
continue;
}
// 重置缓冲区所有空间都可用
cl->buf->pos = cl->buf->start;
cl->buf->last = cl->buf->start;
// 将该空闲区加入到free链表表头
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}
三、graphviz画图工具
graphviz采用dot语言,就感觉像描述一样,随便画出来的图就是这个样子了
看源码梳理函数完全可以用这个,我贴出来一些链接
1.windows下Graphviz安装及入门教程 http://m.blog.csdn.net/article/details?id=49472949
2.利用Graphviz 画结构图
http://www.cnblogs.com/sld666666/archive/2010/06/25/1765510.html
3.使用Graphviz绘制流程图和关系图
http://www.tuicool.com/articles/qeqeuyb
如果有什么问题,欢迎交流

本文详细梳理了nginx源码中ngx_buf_t数据结构的相关内容,包括结构体定义、宏定义和函数声明。同时介绍了用于辅助理解源码的graphviz画图工具及其用法,提供了相关教程链接,帮助读者更好地理解和学习nginx源码。

1036

被折叠的 条评论
为什么被折叠?



