NGINX(一)内存结构

ngx_buf_t和ngx_chain_t是nginx中操作内存的重要手段, 很多的数据都需要通过这个结构进行保存. 其中ngx_buf_t中保存一块可用内存, ngx_chain_t则是将内存块连接起来,组成一个链表, 操作这两个数据结构函数并不多, 下面进行了详解.

基本数据结构

typedef struct ngx_chain_s       ngx_chain_t;
typedef struct ngx_buf_s         ngx_buf_t;
    
struct ngx_buf_s {
	/*当前读取buffer位置*/
	u_char          *pos; 
	/*实际占用buffer的最后位置*/
	u_char          *last;
	/*当前读取文件位置*/
	off_t            file_pos;
	/*文件最后位置*/
	off_t            file_last;
	
	/*buffer开始位置*/
	u_char          *start;
	/*buffer结束位置*/	
	u_char          *end;
	/*内存标记*/
	ngx_buf_tag_t    tag;
	/*文件句柄*/
	ngx_file_t      *file;
	ngx_buf_t       *shadow;
	
	/*1表示内存可以修改*/
	unsigned         temporary:1;
	/*1表示内存不可以修改*/
	unsigned         memory:1;

	/*1表示操作内容通过mmap()函数映射不可修改*/
	unsigned         mmap:1;
	/*1表示内存可以回收*/
	unsigned         recycled:1;
	/*1表示操作内容在文件中*/
	unsigned         in_file:1;
	/*1表示立即将内容发送出去*/
	unsigned         flush:1;
	/*1表示立即将内容同步发送出去*/
	unsigned         sync:1;
	/*标记chain是否为最后一块buf*/
	unsigned         last_buf:1;
	/*标记chain中是否为最后一个chain,最后一块buf并不表示是最后一个chain,但最后一个chain一定是最后一块buf*/
	unsigned         last_in_chain:1;

	unsigned         last_shadow:1;
	unsigned         temp_file:1;

	/* STUB */ int   num;
};

struct ngx_chain_s {
	ngx_buf_t    *buf;
	ngx_chain_t  *next;
};

操作函数

创建一个buf很简单, 直接在内存池中分配一块内存, 然后分配size大小空间, 给ngx_buf_t各个字段进行赋值即可.

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;
}

创建一个buf链表节点, 首先检查内存池pool->chain缓存中是否为空, 不为空直接取出使用, 为空重新分配.

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_buf_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;

	/*首先分配所有buf需要的一整块内存, 没有循环分配小内存, 显然为了提高速度, 处处体现了作者对代码质量和性能的要求之高*/
	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);
		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保存的上一个节点指针的指针, 赋值相当于给上一个节点next指针赋值*/
		*ll = cl;
		/*取本节点next指针地址, 用于下次循环赋值使用*/
		ll = &cl->next;
	}

	*ll = NULL;

	return chain;
}

buf链表添加或者copy, 我觉得这里叫append(附加)可能更好, 因为只是将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;
}

更新链表函数功能是回收busy和out链表中闲置资源, 如果tag标志位与传入的一致, 则链表回收到free链表中, 否则直接释放会pool中, 多用于过滤模块

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;
	}
}
原文地址:https://www.cnblogs.com/ourroad/p/4838794.html