1.预读
作为web服务器,Nginx要频繁地收发处理大量的数据,这些数据有时是连续的内存块,有时是多个分散内存块,甚至有时数据过大,内存无法存放,只能保存成磁盘文件。
作为web服务器,Nginx要频繁地收发处理大量的数据,这些数据有时是连续的内存块,有时是多个分散内存块,甚至有时数据过大,内存无法存放,只能保存成磁盘文件。
2..基本数据结构
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;
};
结构成员
pos: 内存数据的起始位置
last: 内存位置的结束位置
file_pos: 文件数据的起始偏移量
file_last:文件数据的结束偏移量
start: 内存数据的上界
end: 内存数据的下界
tag 关联对象
file 文件对象
因为Nginx里的缓冲数据可能在内存或者磁盘文件中,所以ngx_buf_t使用pos/lastl file_pos/file_last来指定数据在内存或者文件中的具体位置,究竞数据是在哪里则要由后面的标志位信息来确定。
start 和 end 两个成员变量标记了数据所在内存块的边界,如果内存块是可以修改的,那么在操作时必须参考这两个成员防止越界。
tag是一个比较特殊的成员,它的类型是void*,用户可以关联任意数据,在代码中任意解释,通常它指向的是使用该缓冲区的对象。
内存布局
标志位信息
/* 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;
unsigned last_buf:1;
unsigned last_in_chain:1;
unsigned last_shadow:1;
unsigned temp_file:1;
/* STUB */ int num;
结构成员:
temporary 内存块临时数据 可以修改
memory 内存块数据 不可以修改
mmap 内存映射数据 不许修改
in_file 缓冲区在文件里面
flush 要求立马Nginx 立即输出本缓冲区
sync 要求Nginx同步操作本缓冲区
last_buf 最后一块缓冲区
last_in_chain 链里最后一块缓冲区
temp_file 缓冲区在临时文件里
这些标志位的含义都比较好理解,但last_buf和last_in_chain存在一点小差异;前者是整个处理过程中的最后一块缓冲区,标志着TCP/HTTP请求处理的结束;而后者是当前数据块链(ngx_chain_t)里的最后一块,之后可能还会有数据需要处理。
从ngx_buf_t的定义可以看到,一个有数据的缓冲区不是在内存里,就是在文件里,所以内存标志位成员变量(temporary/memory/mmap)和文件标志成员变量(in_file/temp_file)不能全为0,否则Nginx 会认为这是个特殊( special)或无效的缓冲区。
如果缓冲区既不在内存也不在文件里,那么它就不含有有效数据,只起到控制作用 ,例如
刷新( flush )或者同步( sync )
复习一下
采用位域:
位域是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为"位域"或"位段"。所谓"位域"是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
使用位域的好处是:
1.有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。这样节省存储空间,而且处理简便。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。
2.可以很方便的利用位域把一个变量给按位分解。比如只需要4个大小在0到3的随即数,就可以只rand()一次,然后每个位域取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))
ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
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;
}
/*
* set by ngx_calloc_buf():
*
* b->file_pos = 0;
* b->file_last = 0;
* b->file = NULL;
* b->shadow = NULL;
* b->tag = 0;
* and flags
*/
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
b->temporary = 1;
return b;
}
函数返回的ngx_buf_t结构内的成员都已经初始化好了,pos和 last都指向内存块的首位置,表示空缓冲区,而temporary标志位是1。
判断函数
#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap)
#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file)
#define ngx_buf_special(b) \
((b->flush || b->last_buf || b->sync) \
&& !ngx_buf_in_memory(b) && !b->in_file)
1 2函数是判断数据在内存里 采用或运算!!1
3 判断是否是特殊区
计算内存大小
#define ngx_buf_size(b) \
(ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \
(b->file_last - b->file_pos))
1.在内存里面就计算整个内存
2.文件里面就计算文件
拷贝内存(core/ngx_string.h)
拷贝内存数据时我们可以直接使用标准c函数memcpy(),但Nginx自定义了一个函数ngx_cpymem,接口与memcpy()相同,不过它返回的是拷贝数据后的终点位置,在连续夏制多段数据时很方便:
#define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n))