1. ngx_str_t
在nginx源码目录的src/core下面的ngx_string.h|c里面,包含了字符串的封装以及字符串相关操作的api。nginx提供了一个带长度的字符串结构ngx_str_t,它的原型如下:
typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
在结构体当中,data指向字符串数据的第一个字符,字符串的结束用长度来表示,而不是由’\0’来表示结束。所以,在写nginx代码时,处理字符串的方法跟我们平时使用有很大的不一样,但要时刻记住,字符串不以’\0’结束,尽量使用nginx提供的字符串操作的api来操作字符串。
1.1 nginx提供的的操作字符串相关的api:
- #define ngx_string(str) { sizeof(str) - 1, (u_char *) str }
ngx_string(str)是一个宏,它通过一个以’\0’结尾的普通字符串str构造一个nginx的字符串,鉴于其中采用sizeof操作符计算字符串长度,因此参数必须是一个常量字符串。
- #define ngx_null_string { 0, NULL }
定义变量时,使用ngx_null_string初始化字符串为空字符串,符串的长度为0,data为NULL。
- #define ngx_str_set(str, text) \ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
ngx_str_set用于设置字符串str为text,由于使用sizeof计算长度,故text必须为常量字符串。
- #define ngx_str_null(str) (str)->len = 0; (str)->data = NULL
ngx_str_null用于设置字符串str为空串,长度为0,data为NULL。
- void ngx_strlow(u_char *dst, u_char *src, size_t n);
将src的前n个字符转换成小写存放在dst字符串当中,调用者需要保证dst指向的空间大于等于n,且指向的空间必须可写。操作不会对原字符串产生变动。
- ngx_strncmp(s1, s2, n)
区分大小写的字符串比较,只比较前n个字符。
- ngx_strcmp(s1, s2)
区分大小写的不带长度的字符串比较。
- ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);
不区分大小写的不带长度的字符串比较。
- ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
不区分大小写的不带长度的字符串比较。
- ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
不区分大小写的带长度的字符串比较,只比较前n个字符。
2. ngx_poll_t
对于nginx处理的每个http request, nginx会生成一个ngx_pool_t对象与这个http request关联,所有处理过程中需要申请的资源都从这个ngx_pool_t对象中获取,当这个http request处理完成以后,所有在处理过程中申请的资源,都将随着这个关联的ngx_pool_t对象的销毁而释放。
ngx_pool_t相关结构及操作被定义在文件src/core/ngx_palloc.h|c中。
typedef struct ngx_pool_s ngx_pool_t;
struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
2.1 nginx提供的的操作pool相关的api:
- ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
创建一个初始节点大小为size的pool,log为后续在该pool上进行操作时输出日志的对象。 需要说明的是size的选择,size的大小必须小于等于NGX_MAX_ALLOC_FROM_POOL,且必须大于sizeof(ngx_pool_t)。
选择大于NGX_MAX_ALLOC_FROM_POOL的值会造成浪费,因为大于该限制的空间不会被用到(只是说在第一个由ngx_pool_t对象管理的内存块上的内存,后续的分配如果第一个内存块上的空闲部分已用完,会再分配的)。
选择小于sizeof(ngx_pool_t)的值会造成程序崩溃。由于初始大小的内存块中要用一部分来存储ngx_pool_t这个信息本身。
当一个ngx_pool_t对象被创建以后,该对象的max字段被赋值为size-sizeof(ngx_pool_t)和NGX_MAX_ALLOC_FROM_POOL这两者中比较小的。后续的从这个pool中分配的内存块,在第一块内存使用完成以后,如果要继续分配的话,就需要继续从操作系统申请内存。当内存的大小小于等于max字段的时候,则分配新的内存块,链接在d这个字段(实际上是d.next字段)管理的一条链表上。当要分配的内存块是比max大的,那么从系统中申请的内存是被挂接在large字段管理的一条链表上。我们暂且把这个称之为大块内存链和小块内存链。
- void *ngx_palloc(ngx_pool_t *pool, size_t size);
从这个pool中分配一块为size大小的内存。注意,此函数分配的内存的起始地址按照NGX_ALIGNMENT进行了对齐。对齐操作会提高系统处理的速度,但会造成少量内存的浪费。
- void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
从这个pool中分配一块为size大小的内存。但是此函数分配的内存并没有像上面的函数那样进行过对齐。
- void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
该函数也是分配size大小的内存,并且对分配的内存块进行了清零。内部实际上是转调用ngx_palloc实现的。
- void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
按照指定对齐大小alignment来申请一块大小为size的内存。此处获取的内存不管大小都将被置于大内存块链中管理。
- ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
对于被置于大块内存链,也就是被large字段管理的一列内存中的某块进行释放。该函数的实现是顺序遍历large管理的大块内存链表。所以效率比较低下。如果在这个链表中找到了这块内存,则释放,并返回NGX_OK。否则返回NGX_DECLINED。
由于这个操作效率比较低下,除非必要,也就是说这块内存非常大,确应及时释放,否则一般不需要调用。反正内存在这个pool被销毁的时候,总归会都释放掉的嘛!
- ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源。对于这个链表中每个节点所包含的资源如何去释放,是自说明的。这也就提供了非常大的灵活性。意味着,ngx_pool_t不仅仅可以管理内存,通过这个机制,也可以管理任何需要释放的资源,例如,关闭文件,或者删除文件等等。下面我们看一下这个链表每个节点的类型:
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
typedef void (*ngx_pool_cleanup_pt)(void *data);
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;
void *data;
ngx_pool_cleanup_t *next;
};
data: 指明了该节点所对应的资源。
handler: 是一个函数指针,指向一个可以释放data所对应资源的函数。该函数只有一个参数,就是data。
next: 指向该链表中下一个元素。
- void ngx_destroy_pool(ngx_pool_t *pool);