浅谈nginx内存池(四)

1.Nginx的buf缓冲区数据结构,主要用来存储非常大块的内存,ngx_buf_s数据结构也贯穿了整个nginx。

Nginx的缓冲区设计也是非常灵活的:
(1)可以自定义管理业务层面的缓冲区链表。
(2)可以将空闲的缓冲区链表交还给内存池pool->chain结构。

缓冲区ngx_buf_t是nginx处理大数据的关键数据结构,他既应用于内存数据,也应用于磁盘数据。

下面就是关于nginx中buf的实现
---------->buf分为两种类型:
(1)一种是file;
(2)一种是memory;
我们发送往套接字或者其他的设备发送东西时,我们是先将数据放大buf中,然后当设备或者套接字准备好了,我们就会从buf中读取。
ngx_buf_s具体结构如下:
struct ngx_buf_s {
u_char *pos;//待处理数据的开始标记(表示已经执行的数据的位置)

///last和上面内存池中last一样,也就是使用的内存的最后一个字节的指针
u_char *last;//待处理数据的结尾标记

///文件指针(处理文件时,待处理文件的开始标记和结尾标记)
off_t file_pos;
off_t file_last;

///buf的开始指针(缓冲区开始的指针地址和缓冲区结尾的指针地址)
u_char *start; /* start of buffer */
u_char *end; /* end of buffer */

///这里表示这个buf从属于那个模块。
ngx_buf_tag_t tag;//缓冲区标记地址,是一个void*的指针
ngx_file_t *file;//引用的文件
ngx_buf_t *shadow;

///一些标记
/* the buf's content could be changed */
unsigned temporary:1; //标志位,为1时,可修改

///在内存中是不能改变的。
unsigned memory:1; //标志位,为1时,内存只读

///是否是mmap的内存
unsigned mmap:1; //标志位,为1时,mmap映射过来的内存不可修改
unsigned recycled:1; //标志位,为1时,可回收

///一些标志位
unsigned in_file:1; //标志位,为1时,表示处理的是文件
unsigned flush:1; //标志位,为1时,表示需要进行fflush操作
unsigned sync:1;  //标志位,为1时,表示可以进行同步操作,容易引起堵塞
unsigned last_buf:1;//标志位,为1时,表示缓冲区链表ngx_chain_t上的最后一块待处理缓冲区
unsigned last_in_chain:1; //标志位,为1时,表示为缓冲区链表ngx_chain_t上的最后一块缓冲区
unsigned last_shadow:1; //标志位,为1时,表示是否是最后一个影子缓冲区
unsigned temp_file:1; //标志位,为1时,表示当前缓冲区是否属于临时文件
int num;  
};
说明:
(1)从这个数据结构中,可以看出ngx_buf_s结构,既可以处理内存,也可以数据文件。
(2)Nginx使用了位域的方法,节省存储空间。
(3)每个buf都记录了开始和结束点以及未处理的开始和结束点,因为缓冲区的内存申请了之后,是可以被复用的。
(4)所有缓冲区需要的数据结构以及缓冲区的buf内存块都会被分配到内存池上面。

如图:

浅谈nginx内存池(四)

(1)nginx的缓冲区数据结构主要包含了链表数据结构ngx_chain_t和buf数据结构ngx_buf_s。
(2)nginx可以在自定义的业务层面管理繁忙busy和空闲free的缓冲区链表结构,可以对缓冲区的链表结构和buf结构进行管理。
(3)如果缓冲区链表需要被回收,则会放到nginx内存池的pool->chain链表上。
(4)缓冲区是nginx用的非常多的一种数据结构,主要用于接收和输出HTTP的数据信息,所以对nginx的缓冲区的数据结构深入理解非常有必要。

下面就可以看看我们之前在ngx_pool_t中没有说明的一个结构--->ngx_chain_t:
缓冲区链表结构 ngx_chain_t
typedef struct ngx_chain_s       ngx_chain_t;  
/*
 * 缓冲区链表结构,放在pool内存池上面 
 */  
struct ngx_chain_s {  
    ngx_buf_t    *buf;  
    ngx_chain_t  *next;  
};  

1. 是否还记得内存池结构中,有一个数据结构pool->chain就是保存空闲的缓冲区链表的。
2. Nginx的缓冲区ngx_buf_t,通过ngx_chain_t链表结构进行关联和管理。
3. 通过链表的方式实现buf有一个非常大的好处:如果一次需要缓冲区的内存很大,那么并不需要分配一块完整的内存,只需要将缓冲区串起来就可以了。

2.接下来我们来看看如何创建一个buf,在nginx中一般都是调用ngx_create_temp_buf来创建buf。函数也比较简单,就是从pool中分配内存然后初始化相关域。
具体代码如下:
ngx_buf_t * ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
{
ngx_buf_t *b;

///calloc一个buf,可以看到它调用的是calloc,也就是说都会清0.
b = ngx_calloc_buf(pool);
if (b == NULL) {
return NULL;
}

///然后从内存池中分配一块内存。并将这块内存链接到b->start.
b->start = ngx_palloc(pool, size);
/*
最终调用的是内存池pool,开辟一段内存用作缓冲区,主要放置ngx_buf_t 结构体:
//set by ngx_calloc_buf(): 
    *  b->file_pos = 0; 
    *  b->file_last = 0; 
    *  b->file = NULL; 
    *  b->shadow = NULL; 
    *  b->tag = 0; 
    *  and flags 
*/
if (b->start == NULL) {
return NULL;
}

///设置相关的域。
b->pos = b->start;
b->last = b->start;


///设置打消
b->end = b->last + size;
b->temporary = 1;
return b;
}