Nginx自定义数据结构之字符串(String)数组(Array)链表(List)
今天我们一起看下Nginx常用的数据结构。
Nginx基本数据结构有:
- 字符串(string)
- 数组(array)
- 链表(list)
- 队列(queue)
- 散列(hash)
- 红黑树(rbtree)
- 基数树(radix_tree)
一、字符串
这是比较简单 其实和Redis的SDS设计差不多,用一个len字段来保证字符串的二进制安全,解决字符串被"\0"截断的问题。
typedef struct {
size_t len;
u_char *data;
} ngx_str_t;
Nginx 为我们提供了字符串处理相关的宏

二、数组
Nginx的数组占用内存池上一块连续的空间,它存放的数据类型是确定的,而且容量可以扩充,这样的数组更为易用。
文件名:ngx_array.h
typedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_array_t;
字段说明:
elts:数据块,指向实际存储的数据。
nelts:当前数组中已存放数据的数量。
size:每个数据的大小。
nalloc:已经分配的区域大小,即当前数组可存储数据的数量。
pool:存储当前数组的内存池。
文件名:ngx_array.c
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;
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++;
} 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;
}
当存储数组的内存池有剩余空间时,插入数据直接在当前内存池上向后扩容;
当存储数组的内存池空间不足时,需要开辟新的内存空间并将数组数据依次赋值到新内存地址上,以此来保证数组数据的连续性。
当数组使用完毕释放时,直接释放内存池上的空间即可,不用将内存交还给操作系统,从而保证申请和释放内存的高效性。
被释放的内存仍可以另作他用,实现内存的重复利用,减少了系统开销。
三、链表
链表与数组的区别是 它的数据在内存中并不是连续的,但是它插入动作的时间复杂度仅为O(1)。
我们来看下链表的结构
文件名:ngx_list.h
typedef struct ngx_list_part_s ngx_list_part_t;
//链表
struct ngx_list_part_s {
void *elts; // 指向该节点实际的数据区(该数据区中可以存放nalloc个大小为size的元素)
ngx_uint_t nelts; // 实际存放的元素个数(当前节点存放的元素个数)
ngx_list_part_t *next; // 指向下一个节点
};
字段说明:
elts:指向节点数据的实际存储区域。
nelts:当前链表节点上已分配的数据数量。
next:指向链表的下一个节点。
一般的链表一个Node存储一个数据,但是Nginx的链表一个Node上存储了多个数据。
// nginx链表结构 不是一个单纯的链表, 而是一个数组的链表。
typedef struct {
ngx_list_part_t *last; // 指向链表的最后一个数组元素。
ngx_list_part_t part; // 链表的首个数组元素
size_t size; // 每个元素大小
ngx_uint_t nalloc; // 每个节点所包含的数据个数
ngx_pool_t *pool; // 链表所在的内存池
} ngx_list_t;
字段说明:
last:链表最后一个节点。
part:链表的第一个节点。
size:每个数据的大小。
nalloc:链表每个节点所包含的数据个数。
pool:链表头部所在的内存池。
链表的相关操作API有:
// 创建链表
ngx_list_t * ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
// 向链表插入数据
void * ngx_list_push(ngx_list_t *l)
在ngx_list_create 里会调用ngx_list_init初始化链表。
链表初始化完成之后,通过ngx_list_push函数向链表插入数据,函数定义如下:
void *
ngx_list_push(ngx_list_t *l)
{
void *elt;
ngx_list_part_t *last;
last = l->last;
if (last->nelts == l->nalloc) {
/* the last part is full, allocate a new list part */
last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
if (last == NULL) {
return NULL;
}
last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
if (last->elts == NULL) {
return NULL;
}
last->nelts = 0;
last->next = NULL;
l->last->next = last;
l->last = last;
}
// 如果当前节点空间够,那么只会执行这2行
elt = (char *) last->elts + l->size * last->nelts;
last->nelts++;
return elt;
}
不知道大家有没有注意到 链表的插入只传入了一个链表指针, 数组的插入也是只传入了一个数组的指针,并没有传入要插入的数据。
这是因为上层数据类型不定,可以是任何数据类型,所以把数据的拷贝赋值交给上层了,下层只负责分配空间。所以我们可以看到 ngx_list_push、ngx_array_push 的返回值都是 void * 以适配上层的数据类型。
相关推荐
-
unix或centos离线安装jdk和nginx操作说明2023-10-23 15:41:26
-
Nginx自定义数据结构之字符串(String)数组(Array)链表(List)
Nginx自定义数据结构之字符串(String)数组(Array)链表(List)2023-10-23 15:41:09 -
Nginx的配置详解2023-10-23 15:40:54
-
Centos7 Nginx搭建局域网yum仓库详细步骤2023-10-23 15:38:59
-
nginx维护页面处置-全部URL指向同一个页面2023-10-23 15:35:52