Nginx:Buffer缓冲区设计
Posted 看,未来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx:Buffer缓冲区设计相关的知识,希望对你有一定的参考价值。
刚那个长得像deque的链表需要找它的应用场景,缓冲区就不用我多说了吧,前前后后也看了好几个缓冲区的实现,谁的好,就拿来用咯。
缓冲区 Buffer
此前研究了 muduo 的缓冲区设计,有些心得,再来看看 nginx 的缓冲区设计。
typedef struct ngx_buf_s ngx_buf_t;
struct ngx_buf_s {
u_char *pos; //数据处理起始地址
u_char *last; //数据处理结束地址
//以上这俩,不是缓冲区的起止,只是需要被处理的数据的起止,谁用谁自己定义
off_t file_pos;
off_t file_last;
//上面这俩是处理文件用的,其他和前面那俩一样
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 */
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 */
unsigned mmap:1;
unsigned recycled:1; //可回收
unsigned in_file:1; //处理的是文件
unsigned flush:1; //需要刷新
unsigned sync:1; //有些框架代码在sync为1时可能会有阻塞的方式进行I/O操作,它的意义视使用它的Nginx模块而定
unsigned last_buf:1; //因为 ngx_buf_t可以由 ngx_chain_t链表串联起来。last_buf为1时,表示当前是最后一块待处理的缓冲区
unsigned last_in_chain:1; //是否是 ngx_chain_t中的最后一块缓冲区
unsigned last_shadow:1;
unsigned temp_file:1;
/* STUB */ int num;
};
好长哈,而且不知道为什么要来个 typedef,是历史遗留原因?
这是个公用的缓冲区吗?嗯,是从内存池中去申请空间的。
看来需要去完整的看一下 nginx 内存池的设计了,那个会比较重要些吧。
ngx_chain_t
ngx_chain_t是与ngx_buf_t配合使用的链表数据结构。
typedef struct ngx_chain_s ngx_chain_t; //又来
struct ngx_chain_s {
ngx_buf_t *buf; //指向当前的ngx_buf_t缓冲区
ngx_chain_t *next; //来指向下一个ngx_chain_t。
//如果这是最后一个 ngx_chain_t,则需要把next置为NULL。
};
buf指向当前的ngx_buf_t缓冲区,next则用来指向下一个ngx_chain_t。如果这是最后一个 ngx_chain_t,则需要把next置为NULL。 在向用户发送HTTP包体时,就要传入ngx_chain_t链表对象,注意,如果是最后一个 ngx_chain_t,那么必须将next置为NULL,否则永远不会发送成功,而且这个请求将一直不会 结束(Nginx框架的要求)。
(包头存在链表里面)
方法配置
申请临时缓冲区
//申请临时缓冲区
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;
b = ngx_calloc_buf(pool);
if (b == NULL) {
return NULL;
}
b->start = ngx_palloc(pool, size);
if (b->start == NULL) {
return NULL;
}
//目前还是空的,所以这样配备
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
b->temporary = 1; //设置为临时缓冲区
return b;
}
临时的哈,用完还得还的哈。
分配 chain 节点
ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool)
{
ngx_chain_t *cl;
cl = pool->chain;
/* Nginx为了提升效率,会把已经使用过ngx_chain_t保存到ngx_pool_t中以便下次使用 */
if (cl) {
pool->chain = cl->next;
return cl;
}
cl = ngx_palloc(pool, sizeof(ngx_chain_t));
if (cl == NULL) {
return NULL;
}
return cl;
}
分配一个 buf 链表
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;
}
ll = &chain;
for (i = 0; i < bufs->num; i++) {
b = ngx_calloc_buf(pool);
if (b == NULL) {
return NULL;
}
b->pos = p;
b->last = p;
b->temporary = 1;
b->start = p;
p += bufs->size;
b->end = p;
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NULL;
}
cl->buf = b;
*ll = cl;
ll = &cl->next;
}
*ll = NULL;
return chain;
}
合并buf链表
合并buf链表,将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;
for (cl = *chain; cl; cl = cl->next) {
ll = &cl->next;
}
while (in) {
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return NGX_ERROR;
}
cl->buf = in->buf;
//这也要秀一下?
*ll = cl;
ll = &cl->next;
in = in->next;
}
*ll = NULL; //这是为何?
return NGX_OK;
}
从free chain链中获取一个空闲buf
ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free) //@param free 待查找chain链
{
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);
if (cl->buf == NULL) {
return NULL;
}
cl->next = NULL;
return cl;
}
回收链表空间
更新chain链表 释放内存 将busy中空闲节点回到free链表中或者内存池中。
/**
* 更新chain链表 释放内存 将busy中空闲节点回到free链表中或者内存池中
* @param p 内存池
* @param free 空闲链
* @param busy 正在使用链
* @param out 将out中分发到 free或者busy中
* @param tag 标志 分发原则 一般是函数指针
*/
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;
}
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;
*busy = cl->next;
cl->next = *free;
*free = cl;
}
}
你悟到了吗?
这么大块空间,收收发发的不累吗?既然都被申请过了,那就不要收回来了,就放外面吧,下次要用直接拿去。
关于文件操作的我就先不说了哈。。
歇会儿去看内存池去了哈哈
以上是关于Nginx:Buffer缓冲区设计的主要内容,如果未能解决你的问题,请参考以下文章