Nginx的高级数据包括ngx_queue_t, ngx_array_t, ngx_list_t, ngx_rbtree_t, ngx_radix_tree_t, ngx_hash_t 。
1. ngx_queue_t
ngx_queue_t 双向链表是Nginx提供的轻量级链表容器,与Nginx的内存池无关,因此这个链表不会负责分配内存来存放元素,这个数据结构仅仅把已经分配好的内存元素使用双向链表连接起来,所以对每个用户来说,只需要增加两个指针的空间即可,消耗的内存很少。同时,ngx_queue_t 还提供了一个非常简易的插入排序法。相对于Nginx其他顺序容器,它的优势在于:
- 实现了排序功能
- 非常轻量级,是一个纯粹的双向链表,它不负责链表元素所占内存的分配,与Nginx封装的
ngx_pool_t 内存池完全无关。 - 支持两个链表间的合并
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
它的实现非常简单,仅有两个成员prev, next。
实现访问API:
ngx_queue_init(q)
ngx_queue_empty(h)
ngx_queue_insert_head(h, x)
ngx_queue_insert_tail(h, x)
ngx_queue_head(h)
ngx_queue_last(h)
ngx_queue_sentinel(h)
ngx_queue_remove(x)
ngx_queue_split(h, q, n)
ngx_queue_add(h, n)
ngx_queue_middle(h)
ngx_queue_sort(h, compfunc)
对于链表中的每一个元素,其类型可以是任意的struct结构体,但这个结构体中必须要有一个ngx_queue_t 类型的成员,在向链表容器中添加、删除时都是使用结构体中的ngx_queue_t类型成员的指针。当ngx_queue_t作为链表的元素成员使用时,他具有下面4种用法:
ngx_queue_next(q)
ngx_queue_prev(q)
ngx_queue_data(q, type, link)
ngx_queue_insert_after(q, x)
Test SampleCode:
#include <stdio.h>
#include <string.h>
#include "ngx_config.h"
#include "ngx_core.h"
#include "ngx_list.h"
#include "ngx_palloc.h"
#include "ngx_string.h"
#include "ngx_queue.h"
ngx_queue_t queueContainer;
typedef struct {
u_char *str;
ngx_queue_t qEle;
int num;
}TestNode;
ngx_int_t compTestNode(const ngx_queue_t* a, const ngx_queue_t *b) {
TestNode *aNode = ngx_queue_data(a, TestNode, qEle);
TestNode *bNode = ngx_queue_data(b, TestNode, qEle);
return aNode->num > bNode->num;
}
volatile ngx_cycle_t *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log,
ngx_err_t err, const char *fmt, ...)
{
}
int main() {
TestNode node[5];
ngx_queue_init(&queueContainer);
for (int i = 0; i < 5; i++) {
node[i].num = i;
}
ngx_queue_insert_tail(&queueContainer, &node[1].qEle);
ngx_queue_insert_tail(&queueContainer, &node[4].qEle);
ngx_queue_insert_tail(&queueContainer, &node[0].qEle);
ngx_queue_insert_tail(&queueContainer, &node[2].qEle);
ngx_queue_insert_tail(&queueContainer, &node[3].qEle);
ngx_queue_sort(&queueContainer, compTestNode);
ngx_queue_t *q;
for (q = ngx_queue_head(&queueContainer); q != ngx_queue_sentinel(&queueContainer); q = ngx_queue_next(q)) {
TestNode *eleNode = ngx_queue_data(q, TestNode, qEle);
printf("%d\n", eleNode->num);
}
return 0;
}
编译:
gcc -o ngx_queue_main ngx_queue_main.c -I ../../nginx-1.16.1/src/core/ -I ../../nginx-1.16.1/objs/ -I ../../nginx-1.16.1/src/os/unix/ -I ../../pcre-8.44/ -I ../../nginx-1.16.1/src/event/ ../../nginx-1.16.1/objs/src/core/ngx_list.o ../../nginx-1.16.1/objs/src/core/ngx_string.o ../../nginx-1.16.1/objs/src/core/ngx_palloc.o ../../nginx-1.16.1/objs/src/os/unix/ngx_alloc.o../../nginx-1.16.1/objs/src/core/ngx_queue.o
2. ngx_array_t
ngx_array_t 是一个顺序的动态数组,在Nginx大量使用,支持达到数组容量上限时动态改变数组的大小。ngx_array_t 和C++ STL中的vector很类似,它内置了Nginx封装的内存池,因此,他分配的内存可以在内存池中申请得到。具备下面几个特点:
- 访问速度快
- 允许元素个数具备不确定性
- 负责元素占用内存的分配,这些内存由内存池统一管理
typedef struct {
void *elts;
ngx_uint_t nelts;
size_t size;
ngx_uint_t nalloc;
ngx_pool_t *pool;
} ngx_array_t;
提供了如下API:
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size);
ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
ngx_array_destroy(ngx_array_t *a);
ngx_array_push(ngx_array_t *a);
ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
动态数组的扩容方式:
ngx_array_push 和ngx_array_push_n 都可能引发扩容操作,ngx_array_push 方法将会申请 ngx_array_t 结构体中size字节的大小,而ngx_array_push_n 方法将会申请n个size字节大小的内存。每次扩容的大小将受制于内存池的以下两种情形:
- 如果当前内存池中剩余的空间的大于或者等于本次需要新增的空间,那么本次扩容将只扩容新增的空间。例如: 对于
ngx_array_push 来说,就是扩充1个元素,而对于ngx_array_push_n 来说,就是扩充n个元素。 - 如果当前内存池中剩余的空间小于本次需要新增的空间,那么对
ngx_array_push 方法来说,会将原先动态数组的容量扩容一倍,而对于ngx_array_push_n 来说,如果参数n 小于原先动态数组的容量,将会扩容一倍;如果参数n 大于原先动态数组的容量,这时会分配2n 大小的空间,扩容超过一倍,此时动态数组会被分配在全新的内存块上,会把原先的元素复制到新的动态数组中。
SampleCode:
#include <stdio.h>
#include <string.h>
#include "ngx_config.h"
#include "ngx_core.h"
#include "ngx_list.h"
#include "ngx_palloc.h"
#include "ngx_string.h"
#define N 10
typedef struct _key {
int id;
char name[32];
} Key;
volatile ngx_cycle_t *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log,
ngx_err_t err, const char *fmt, ...)
{
}
void print_array(ngx_array_t *array) {
Key *key = array->elts;
int i = 0;
for (i = 0;i < array->nelts;i ++) {
printf("%s .\n", key[i].name);
}
}
int main() {
ngx_pool_t *pool = ngx_create_pool(1024, NULL);
ngx_array_t *array = ngx_array_create(pool, N, sizeof(Key));
int i = 0;
Key *key = NULL;
for (i = 0;i < 25;i ++) {
key = ngx_array_push(array);
key->id = i+1;
sprintf(key->name, "array %d", key->id);
}
key = ngx_array_push_n(array, 10);
for (i = 0;i < 10;i ++) {
key[i].id = 25+i+1;
sprintf(key[i].name, "array %d", key[i].id);
}
print_array(array);
}
编译:
gcc -o ngx_array_main ngx_array_main.c -I ../../nginx-1.16.1/src/core/ -I ../../nginx-1.16.1/objs/ -I ../../nginx-1.16.1/src/os/unix/ -I ../../pcre-8.44/ -I ../../nginx-1.16.1/src/event/ ../../nginx-1.16.1/objs/src/core/ngx_string.o ../../nginx-1.16.1/objs/src/core/ngx_palloc.o ../../nginx-1.16.1/objs/src/os/unix/ngx_alloc.o ../../nginx-1.16.1/objs/src/core/ngx_array.o
3. ngx_rbtree_t
ngx_rbtree_t 是使用红黑树实现的一种关联容器,Nginx模块的核心(定时器管理、文件缓存模块等)在需要快速检索、查找的场合下都使用了ngx_rbtree_t 容器。
typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;
struct ngx_rbtree_node_s {
ngx_rbtree_key_t key;
ngx_rbtree_node_t *left;
ngx_rbtree_node_t *right;
ngx_rbtree_node_t *parent;
u_char color;
u_char data;
};
typedef struct ngx_rbtree_s ngx_rbtree_t;
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
struct ngx_rbtree_s {
ngx_rbtree_node_t *root;
ngx_rbtree_node_t *sentinel;
ngx_rbtree_insert_pt insert;
};
添加数据的三种API:
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
红黑树的API:
ngx_rbtree_init(tree, s, i)
ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
在初始化红黑树的时候,需要先分配好保存在红黑树的ngx_rbtree_t 结构体,以及ngx_rbtree_node_t 类型的哨兵节点,并选择或者自定义ngx_rbree_insert_pt 类型的节点添加函数。
Samplecode:
#include <stdio.h>
#include <string.h>
#include "ngx_config.h"
#include "ngx_core.h"
#include "ngx_list.h"
#include "ngx_palloc.h"
#include "ngx_string.h"
#include "ngx_rbtree.h"
volatile ngx_cycle_t *ngx_cycle;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log,
ngx_err_t err, const char *fmt, ...)
{
}
int main() {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
ngx_rbtree_init(&rbtree, &sentinel, ngx_str_rbtree_insert_value);
ngx_str_node_t strnode[10];
ngx_str_set(&strnode[0].str, "he");
ngx_str_set(&strnode[1].str, "jon");
ngx_str_set(&strnode[2].str, "Issac");
ngx_str_set(&strnode[3].str, "tursom");
ngx_str_set(&strnode[4].str, "will");
ngx_str_set(&strnode[5].str, "birate");
ngx_str_set(&strnode[6].str, "ren");
ngx_str_set(&strnode[7].str, "stephen");
ngx_str_set(&strnode[8].str, "ultimate");
ngx_str_set(&strnode[9].str, "he");
int i = 0;
for (i = 0;i < 10;i ++) {
strnode[i].node.key = i;
ngx_rbtree_insert(&rbtree, &strnode[i].node);
}
ngx_str_t str = ngx_string("will");
ngx_str_node_t *node = ngx_str_rbtree_lookup(&rbtree, &str, 0);
if (node != NULL) {
printf(" Exit\n");
}
}
编译:
gcc -o ngx_array_main ngx_array_main.c -I ../../nginx-1.16.1/src/core/ -I ../../nginx-1.16.1/objs/ -I ../../nginx-1.16.1/src/os/unix/ -I ../../pcre-8.44/ -I ../../nginx-1.16.1/src/event/ ../../nginx-1.16.1/objs/src/core/ngx_string.o ../../nginx-1.16.1/objs/src/core/ngx_palloc.o ../../nginx-1.16.1/objs/src/os/unix/ngx_alloc.o ../../nginx-1.16.1/objs/src/os/unix/ngx_rbtree.o
4. ngx_hash_t (待更新)
本文部分技术点出处,Linux C/C++服务器直播视频: 推荐免费订阅
|