nginx之ngx_radix_tree
ngx_radix_tree.h:
//ngx_radix_node_t的定义:
typedef struct ngx_radix_node_s ngx_radix_node_t;
struct ngx_radix_node_s {
ngx_radix_node_t *right;
ngx_radix_node_t *left;
ngx_radix_node_t *parent;
uintptr_t value;
};
//ngx_radix_tree_t的定义:
typedef struct {
ngx_radix_node_t *root;
ngx_pool_t *pool;
ngx_radix_node_t *free;
char *start;
size_t size;
} ngx_radix_tree_t;
由于radix tree实现较为简单,且nginx并不像linux使用64节点的radix tree来实现,从整体上看,ngx_radix_tree跟二叉树类似,只不过通过bit位来确定该往左子树(0为左)还是右子树(1为右),最终在叶结点找到所要查找的数据。
ngx_radix_tree.c:
static ngx_radix_node_t *
ngx_radix_alloc(){
...
if (tree->size < sizeof(ngx_radix_node_t)) {
tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);
if (tree->start == NULL) {
return NULL;
}
tree->size = ngx_pagesize;
}
p = (ngx_radix_node_t *) tree->start;
tree->start += sizeof(ngx_radix_node_t);
tree->size -= sizeof(ngx_radix_node_t);
...
}
ngx_alloc申请一个节点,先访问是否存在可回收的节点,tree->free,接着判断剩余size是否足够申请一个节点,如果不够就从内存池里申请pagesize(一页4096字节),返回节点的起始地址。
uintptr_t
ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
{
...
bit = 0x80000000;
value = NGX_RADIX_NO_VALUE;
node = tree->root;
while (node) {
if (node->value != NGX_RADIX_NO_VALUE) {
value = node->value;
}
if (key & bit) {
node = node->right;
} else {
node = node->left;
}
bit >>= 1;
}
return value;
}
查找对于radixtree来说是比较简单的,根据bit来决定向左或者向右。
ngx_int_t
ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask, uintptr_t value)
{
...
bit = 0x80000000;
node = tree->root;
next = tree->root;
while (bit & mask) {
if (key & bit) {
next = node->right;
} else {
next = node->left;
}
if (next == NULL) {
break;
}
bit >>= 1;
node = next;
}
if (next) {
if (node->value != NGX_RADIX_NO_VALUE) {
return NGX_BUSY;
}
node->value = value;
return NGX_OK;
}
while (bit & mask) {
next = ngx_radix_alloc(tree);
if (next == NULL) {
return NGX_ERROR;
}
next->right = NULL;
next->left = NULL;
next->parent = node;
next->value = NGX_RADIX_NO_VALUE;
if (key & bit) {
node->right = next;
} else {
node->left = next;
}
bit >>= 1;
node = next;
}
node->value = value;
return NGX_OK;
}
ngx_radix_tree插入也是较为简单,从root开始寻找,找到空位的时候向里面插入即可。
ngx_int_t
ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)
{
...
bit = 0x80000000;
node = tree->root;
//寻找要删除的节点
while (node && (bit & mask)) {
if (key & bit) {
node = node->right;
} else {
node = node->left;
}
bit >>= 1;
}
...
//如果node有左节点或者右节点的话只将其value置为-1
if (node->right || node->left) {
if (node->value != NGX_RADIX_NO_VALUE) {
node->value = NGX_RADIX_NO_VALUE;
return NGX_OK;
}
return NGX_ERROR;
}
//node是叶结点的话,则将其置入tree->free链表中
for ( ;; ) {
if (node->parent->right == node) {
node->parent->right = NULL;
} else {
node->parent->left = NULL;
}
node->right = tree->free;
tree->free = node;
node = node->parent;
...
}
return NGX_OK;
}
ngx_radix_tree_t *
ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
{
...
//直接从内存池里面分配内存
tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
if (tree == NULL) {
return NULL;
}
tree->pool = pool;
tree->free = NULL;
tree->start = NULL;
tree->size = 0;
//分配root节点
tree->root = ngx_radix_alloc(tree);
if (tree->root == NULL) {
return NULL;
}
tree->root->right = NULL;
tree->root->left = NULL;
tree->root->parent = NULL;
tree->root->value = NGX_RADIX_NO_VALUE;
...
/**
*如果没有preallocate==0的话,那么就可以返回该tree
*但是一般radix tree是会在开始的时候预分配节点
*而nginx会根据是否preallocate==-1时自动根据平台来初始化
*/
if (preallocate == -1) {
switch (ngx_pagesize / sizeof(ngx_radix_node_t)) {
/* amd64 */
case 128:
preallocate = 6;
break;
/* i386, sparc64 */
case 256:
preallocate = 7;
break;
/* sparc64 in 32-bit mode */
default:
preallocate = 8;
}
}
/**
*创建提前分配的节点时,使用了掩码mask来进行简化创建
*/
mask = 0;
inc = 0x80000000;
while (preallocate--) {
key = 0;
mask >>= 1;
mask |= 0x80000000;
do {
if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)
!= NGX_OK)
{
return NULL;
}
key += inc;
} while (key);
inc >>= 1;
}
...
}
流程图如下:
第一次的时候,key为0,inc为0x80000000,创建完左右节点,key溢出,退出do…while的循环;
接下去,key为0,mask为0xc0000000,inc为0x40000000,则会在每个root的节点中创建出属于自己的两个节点,创建之后,由于key溢出,退出do…while循环;
最终nginx会创建深度为preallocate的一颗树。