为了保证操作的高效性以及对内存的严格控制,nginx自己封装了很多基础的数据结构,这些基本的数据类型在nginx的源码中随处可见,熟悉和掌握各种类型的用法是了解nginx底层原理的基础,下面就一起看看这些独具特色的ngx系风格的数据类型。
(以下未做注释的属性成员都是不太重要的,可忽略。)
ngx_str_t
相关函数:
初始化:
ngx_str_null(&local_conf->hello_string);
赋值:
ngx_str_set(&r->headers_out.content_type, "text/html");
ngx_buf_t
描述:缓冲区ngx_buf_t是nginx处理大数据的关键,它既应用于内存数据也应用于磁盘数据,ngx_buf_t本质上提供了一些指针成员和标志位。
位置: ngx_buf.h
定义:
struct ngx_buf_s {
u_char *pos; /* pos是告诉使用者本次应该从pos这个位置开始处理内存中的数据,因为同一个ngx_buf_t可能被多次反复处理,pos的作用类似文件处理过程中的文件指针*/
u_char *last; /* last 表示有效的内容到此为止, 一般pos到last之间的内容是希望nginx处理的内容*/
off_t file_pos; /* 处理文件时,file_pos和file_last作用和post,last作用类似*/
off_t file_last;
u_char *start; /* start of buffer */
u_char *end; /* end of buffer */
ngx_buf_tag_t tag; /* 表示当前缓冲区的类型,例如由哪个模块使用就指向这个模块ngx_module_t变量的地址*/
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; /* 标志位:为1时表示这段内存是由mmap系统映射过来的,不可以被修改*/
unsigned recycled:1; /* 标志位:为1时表示这段内存可回收*/
unsigned in_file:1; /* 标志位:为1时表示处理的这段缓冲区是文件而不是内存*/
unsigned flush:1; /* 标志位:为1时表示需要执行flush操作*/
unsigned sync:1; /* 标志位:为1时表示以同步的方式处理这块内存,需谨慎*/
unsigned last_buf:1; /* 标志位:表示是否是最后一块缓冲区*/
unsigned last_in_chain:1; /* 标志位:表示是否是ngx_chain_t中的最后一块缓冲区*/
unsigned last_shadow:1; /*标志位:表示是否是最后一个影子缓冲区,和shadow配合使用*/
unsigned temp_file:1; /*标志位:表示当前缓存区是否属于临时文件*/
/* STUB */ int num;
};
常用方法:
ngx_palloc(ngx_pool_t *pool, size_t size)
ngx_pcalloc(r->pool, 1024)
ngx_create_temp_buf(r->pool,1024)
ngx_file_t
描述:nginx通过ngx_file_t结构体来描述某个文件
位置: ngx_file.h
struct ngx_file_s {
ngx_fd_t fd; //文件句柄描述符
ngx_str_t name; //文件名称
ngx_file_info_t info; //文件大小等资源信息,实际就是Linux系统定义的stat结构
off_t offset; //该偏移量告诉Nginx现在文件处理到哪里了,一般不用设置它,Nginx框架会根据当前发送状态设置它
off_t sys_offset; //当前文件系统偏移量,一般不用设置它,同样由nginx框架设置
ngx_log_t *log; //日志对象,相关的日志文件会输出到log指定的日志文件中
#if (NGX_THREADS || NGX_COMPAT)
ngx_int_t (*thread_handler)(ngx_thread_task_t *task,
ngx_file_t *file);
void *thread_ctx;
ngx_thread_task_t *thread_task;
#endif
#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)
ngx_event_aio_t *aio;
#endif
unsigned valid_info:1;
unsigned directio:1;
};
ngx_pool_cleanup_t
描述:对于nginx打开的文件句柄,请求执行完毕后,需要及时关闭掉,否则会出现句柄泄露
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler; //执行清理资源工作的回调方法
void *data; // handler方法需要的参数
ngx_pool_cleanup_t *next; //下一个需要清理的对象
};
ngx_array_t
动态数组:
typedef struct {
void *elts; // 数组元素的起始地址
ngx_uint_t nelts; // 当前已经存放的数组元素个数
size_t size; // 每个元素所占用的空间大小
ngx_uint_t nalloc; // 数组中能存放的元素总数
ngx_pool_t *pool; // 该数组所属的内存池地址
} ngx_array_t;
nginx提供的五个方法:
- 创建数组,并分配n个大小为size的空间
ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)
{
ngx_array_t *a;
// 先申请结构体ngx_array_t的内存
a = ngx_palloc(p, sizeof(ngx_array_t));
if (a == NULL) {
return NULL;
}
// 然后调用ngx_array_init申请各元素的内存空间
if (ngx_array_init(a, p, n, size) != NGX_OK) {
return NULL;
}
return a;
}
- 销毁数组
void
ngx_array_destroy(ngx_array_t *a)
{
ngx_pool_t *p;
p = a->pool;
// 如果内存池的起始可用地址正好是数组a的结束地址,则直接把起始可用地址往前移动数组的长度大小位置即可
if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
p->d.last -= a->size * a->nalloc;
}
// 继续将p的last指针往前移动一个ngx_array_t 结构体的大小位置,回收掉结构体本身占用的那部分内存
if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {
p->d.last = (u_char *) a;
}
}
- 数组初始化
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
array->nelts = 0;
array->size = size;
array->nalloc = n;
array->pool = pool;
array->elts = ngx_palloc(pool, n * size);
if (array->elts == NULL) {
return NGX_ERROR;
}
return NGX_OK;
}
- 添加一个元素
void *
ngx_array_push(ngx_array_t *a)
{
void *elt, *new;
size_t size;
ngx_pool_t *p;
// 数组中元素已经达到了最大值,需要扩容
if (a->nelts == a->nalloc) {
/* the array is full */
size = a->size * a->nalloc;
p = a->pool;
// 如果内存池中剩余的空间比一个数组元素还大,则将内存池的last指针往后移动一个单位。
if ((u_char *) a->elts + size == p->d.last
&& p->d.last + a->size <= p->d.end)
{
/*
* the array allocation is the last in the pool
* and there is space for new allocation
*/
p->d.last += a->size;
a->nalloc++;
// 当前内存池剩余空间已不足以容纳一个元素,则需要重新申请动态数组,并且是申请的空间大小是原来的2倍,然后将旧数组中的元素拷贝至新数组,这个过程可能会耗时较长
} else {
/* allocate a new array */
new = ngx_palloc(p, 2 * size);
if (new == NULL) {
return NULL;
}
ngx_memcpy(new, a->elts, size);
a->elts = new;
a->nalloc *= 2;
}
}
elt = (u_char *) a->elts + a->size * a->nelts;
a->nelts++;
return elt;
}
- 添加n个元素
void *
ngx_array_push_n(ngx_array_t *a, ngx_uint_t n)
{
void *elt, *new;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *p;
size = n * a->size;
if (a->nelts + n > a->nalloc) {
/* the array is full */
p = a->pool;
if ((u_char *) a->elts + a->size * a->nalloc == p->d.last
&& p->d.last + size <= p->d.end)
{
/*
* the array allocation is the last in the pool
* and there is space for new allocation
*/
p->d.last += size;
a->nalloc += n;
} else {
/* allocate a new array */
nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);
new = ngx_palloc(p, nalloc * a->size);
if (new == NULL) {
return NULL;
}
ngx_memcpy(new, a->elts, a->nelts * a->size);
a->elts = new;
a->nalloc = nalloc;
}
}
elt = (u_char *) a->elts + a->size * a->nelts;
a->nelts += n;
return elt;
}
说明: ngx_array_push和ngx_array_push_n都可能触发数组的扩容操作,扩容的时候会判断当前内存池是否有足够的空间,如果有,则只需要扩容一个或者n个元素大小的空间;如果内存不足够,则会重新申请动态数组的空间,并且申请的空间大小为原来的2倍,并将元素从旧数组拷贝至新数组中。