Nginx数据结构(一)—— ngx_str_t、ngx_pool_t
ngx_str_t
ngx_str_t
是一个带长度的字符串结构:
typedef struct
{
size_t len;
u_char *date;
}
- date指向字符串的第一个字符,字符串的结束用长度表示,而不是’\0’。Nginx之所以这样做,首先就是为了减少计算字符串长度的次数。其次,就是可以靠这个机制去重复引用一个字符串,而不是去拷贝一份。所以,在Nginx中,修改一个字符串必须小心!!
- 因为Nginx采用了与glibc完全不同的设计模式,所以在调用一些函数就不能直接传入
str->data
,需要去copy
字符串。
操作字符串相关API
ngx_string(str);
//将'\0'结尾的字符串转换为Nginx字符串
//但是,因为要用sizeof计算操作符长度,所以需要参数必须为常量字符串。
ngx_null_string;
//声明变量时,初始化字符串为空字符串,字符串长度为0,data为null
ngx_str_string(&ser, text);
//设置字符串str为text,text必须是常量字符串
ngx_str_null(&ser);
//设置字符串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);
//区分大小写的带长度的字符串比较,只比较前n个字符
使用案例
//ngx_string和ngx_null_string只能用于赋值初始化
ngx_str_t str = ngx_string("hello world");
ngx_str_t str1 = ngx_null_string();
//如果向下面这样使用就会出错
ngx_str_t str,str1;
str = ngx_string("hello world"); //编译出错
str1 = ngx_null_string(); //编译出错
//这时,就需要调用这两个函数来做
ngx_str_t str,str1;
ngx_str_set(&str, "hello world");
ngx_str_null(&str1);
//但是这里传入的字符串也必须是常量
ngx_str_t str;
u_char* a = "hello world";
ngx_str_set(&str, 0); //errno
ngx_str_t str = ngx_string("hello world");
ngx_strlow(str->data, str->data, str->len);
ngx_pool_t
typedef struct ngx_pool_s ngx_pool_t;
struct ngx_pool_s
{
ngx_pool_data_s d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *cleanup;
ngx_log_t *log;
};
ngx_pool_t
提供了一种机制,帮助管理一系列的资源,使得对这些资源的使用和释放统一进行,免除了使用过程中考虑到对各种各样资源的什么时候释放,是否遗漏了释放的担心。
池的概念引入内存管理:
如果我们需要使用内存,那么总是从一个ngx_pool_t
对象中去取得一块内存,最终再对这个“池”进行 销毁。
当我们使用ngx_pool_t这个数据结构的时候,所有的资源的释放都在这个对象销毁的时候,进行统一的释放。但是这样就会带来一个问题: 这些资源的生存周期和ngx_pool_t的生存周期基本一致,这一定程度的影响了效率 。这就是有所得,必有所失。
相关操作API
ngx_pool_t* ngx_create_pool(size_t size, ngx_log_t *log);
//创建一个初始节点大小为size的pool,log为后续在该pool上进行操作时输出日志的对象
在这里需要特别注意传入参数size的大小,必须小于等于NGX_MAX_ALLOC_FROM_POOL
,且必须大于sizeof(ngx_pool_t)
。
当一个ngx_pool_t
对象被创建之后,该对象的max字段被赋值为size-sizeof(ngx_pool_t)
和NGX_MAX_ALLOC_FROM_POOL
两者中比较小的。后续的从这个pool中分配的内存块,在第一块内存使用完后,如果要继续分配的话,就需要继续从操作系统中申请内存。当内存的大小小于等于max字段的时候,则分配的新的内存块,链接在ngx_pool_data_s d
这个资源管理的一条链表上。
void* ngx_palloc(ngx_pool_t* pool, size_t size);
//从这个pool中分配一块为size大小的内存,会进行地址对齐
void* ngx_pnalloc(ngx_pool_t* pool, size_t size);
//从这个pool中分配一块为size大小的内存,不会进行地址对齐
void* ngx_pcalloc(ngx_pool_t* pool, size_t size);
//从这个pool中分配一块为size大小的内存,但是会清零,其实,内部也是转调ngx_palloc实现
void* ngx_prealloc(ngx_pool_t* pool, void* p, size_t old_size, size_t newsize);
//对p指向的内存进行二次分配,类似于vector的扩容
void* ngx_pmemalign(ngx_pool_t* pool, size_t size, size_t aligment);
//按照指定对其大小alignmen来申请一块大小为size的内存。此处获取的内存不管大小都被置于大内存块链中管理
ngx_int_t ngx_pfee(ngx_pool_t* pool, void* p);
//对被置于大块内存链,也就是large字段管理的一段内存中的某块进行释放,效率较为低下
ngx_pool_cleanup_t* ngx_pool_cleanup_add(ngx_pool_t* p,size_t size);
//cleanup字段管理着一个特殊的需要释放的资源链表
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 handlder; //指向释放data所对应资源的函数
void *data;
ngx_pool_cleanup_t *next;
}
//这里的size指向存储这个data指向资源的大小。
比如说,我们要删除一个文件,那么我们就把size指定为存储文件名的字符串的大小,然后调用这个函数给cleanup链表中增加一项。
该函数会返回新添加的这个节点,然后把这个节点中的data字段拷贝为文件名。
void ngx_destory_pool(ngx_pool_t* pool);
//释放pool中持有的所有内存,然后依次调用cleanup里面的处理函数
void ngx_reset_pool(ngx_pool_t* pool);
//释放pool中所有的大块内存链表上的内存,小块链上内存都修改为可用,但是不会去处理cleanup链表上的项目
参考文献
[1] Nginx开发从入门到精通