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开发从入门到精通
全部评论

相关推荐

11-28 17:48
中山大学 C++
点赞 评论 收藏
分享
评论
1
1
分享
牛客网
牛客企业服务