Nginx 原始碼完全剖析(10)ngx_radix_tree
Nginx 原始碼完全剖析(10)ngx_radix_tree
- 作者:鍾超
- 部落格:http://Blog.CSDN.net/Poechant
- 郵箱:zhongchao.ustc#gmail.com (#->@)
- 日期:2012年10月12日
ngx_radix_tree.h
// 未被使用的節點
#define NGX_RADIX_NO_VALUE (uintptr_t) -1
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; // 值域
};
typedef struct {
ngx_radix_node_t *root; // 樹根
ngx_pool_t *pool; // 該樹所用的記憶體池
ngx_radix_node_t *free; // 空閒的節點由free開始連成一個連結串列,節點間通過right指標連線
char *start;
size_t size;
} ngx_radix_tree_t;
ngx_radix_tree.c
static void *ngx_radix_alloc(ngx_radix_tree_t *tree);
ngx_radix_tree_t *
ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
{
uint32_t key, mask, inc;
ngx_radix_tree_t *tree;
// 為該樹的結構體分配記憶體
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;
// 為根節點分配記憶體(實際上不一定有重新的記憶體分配操作,具體詳見ngx_radix_alloc部分)
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;
// 如果指定的預分配節點數為 0,則直接返回這個樹就好了
if (preallocate == 0) {
return tree;
}
/*
* Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
* increases TLB hits even if for first lookup iterations.
* On 32-bit platforms the 7 preallocated bits takes continuous 4K,
* 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits
* takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to
* to preallocate more than one page, because further preallocation
* distributes the only bit per page. Instead, a random insertion
* may distribute several bits per page.
*
* Thus, by default we preallocate maximum
* 6 bits on amd64 (64-bit platform and 4K pages)
* 7 bits on i386 (32-bit platform and 4K pages)
* 7 bits on sparc64 in 64-bit mode (8K pages)
* 8 bits on sparc64 in 32-bit mode (8K pages)
*/
// 下面這部分就很有意思了,你可以看上面的英文註釋。簡單說,一個 x bits 的值,對應其 Radix 樹
// 有 x + 1 層,那麼節點的個數就是 2^(x+1) -1 個(資料結構常識,你也可以很容易證明這個結論)。
if (preallocate == -1) {
// 根據 pagesize 大小,確定可以分配多少個 radix 樹結構
switch (ngx_pagesize / sizeof(ngx_radix_tree_t)) {
/* amd64 */
case 128:
preallocate = 6;
break;
/* i386, sparc64 */
case 256:
preallocate = 7;
break;
/* sparc64 in 32-bit mode */
default:
preallocate = 8;
}
}
mask = 0;
inc = 0x80000000;
// preallocate 為幾,最終 mask 就有幾個最高位為1,其他為0。整個迴圈過程中 mask 不斷右移並在
// 最高位添置新 1。
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;
}
return tree;
}
// mask 為掩碼,用於擷取 key 中的部分位元位,將其插入到 tree 數中,對應的值為 value
ngx_int_t
ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask,
uintptr_t value)
{
uint32_t bit;
ngx_radix_node_t *node, *next;
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;
}
// next 不為 NULL,是因 bit & mask 為 0 退出上面的 while 的
if (next) {
if (node->value != NGX_RADIX_NO_VALUE) {
return NGX_BUSY;
}
node->value = value;
return NGX_OK;
}
// next 為 NULL,從 tree 新分配一個節點
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;
}
// 節點從 Radix 樹中刪除後,會放入到 free 連結串列中
ngx_int_t
ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)
{
uint32_t bit;
ngx_radix_node_t *node;
bit = 0x80000000;
node = tree->root;
while (node && (bit & mask)) {
// key 該位為 1,表示接下來找右子樹
if (key & bit) {
node = node->right;
// key 該位為 0,表示接下來找左子樹
} else {
node = node->left;
}
bit >>= 1;
}
// 要刪除的節點不存在
if (node == NULL) {
return NGX_ERROR;
}
// 要刪除的節點還有子節點
if (node->right || node->left) {
if (node->value != NGX_RADIX_NO_VALUE) {
node->value = NGX_RADIX_NO_VALUE;
return NGX_OK;
}
// 要刪除的節點有子樹,但是該節點的值為無效值,則視為錯誤
return NGX_ERROR;
}
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;
if (node->right || node->left) {
break;
}
if (node->value != NGX_RADIX_NO_VALUE) {
break;
}
// node 為根節點
if (node->parent == NULL) {
break;
}
}
return NGX_OK;
}
// 在 tree 樹中查詢 key 值,key 是一個無符號的32位整數,每一位對應從樹根開始
// 查詢時選擇左子樹(0)還是右子樹(1)
uintptr_t
ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
{
uint32_t bit;
uintptr_t value;
ngx_radix_node_t *node;
// 初始狀態下最高位為1,用於後面的“與”操作,確定左右子樹
bit = 0x80000000;
value = NGX_RADIX_NO_VALUE;
node = tree->root; // 從樹根開始
// 理論上最多迴圈32次(key為32位),實際上查詢到node為NULL,則表明上一輪迴圈中已經是葉子節點
while (node) {
if (node->value != NGX_RADIX_NO_VALUE) {
value = node->value;
}
// 該位為 1 則右子樹
if (key & bit) {
node = node->right;
// 該位為 0 則左子樹
} else {
node = node->left;
}
bit >>= 1;
}
// 返回找到的節點的值
return value;
}
static void *
ngx_radix_alloc(ngx_radix_tree_t *tree)
{
char *p;
// 建立Radix樹時會呼叫,此時free為NULL,不會進入該if分支
// 插入時呼叫到這裡,free 值非零,則返回 free
if (tree->free) {
p = (char *) tree->free;
tree->free = tree->free->right;
return p;
}
// 建立Radix樹時會呼叫,此時tree->size為0,會進入該if分支
if (tree->size < sizeof(ngx_radix_node_t)) {
// 以ngx_pagesize大小記憶體對齊的方式,從記憶體池tree->pool中分配ngx_pagesize大小的記憶體給start
// ngx_pagesize 是在 src/os/unix/ngx_posix_init.c 和 src/os/win32/ngx_win32_init.c
// 的 ngx_os_init() 函式中初始化的。pagesize 的值與處理器架構有關。
tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);
if (tree->start == NULL) {
return NULL;
}
// tree->size 為剛才分配的記憶體大小
tree->size = ngx_pagesize;
}
// tree->start 加上 ngx_radix_node_t 將要佔用的大小
// tree->size 減去 ngx_radix_node_t 將要佔用的大小
p = tree->start;
tree->start += sizeof(ngx_radix_node_t);
tree->size -= sizeof(ngx_radix_node_t);
// 雖然返回值型別是 void*,但是呼叫處都會轉為 ngx_radix_node_t
return p;
}
-
轉載請註明來自鍾超的CSDN部落格:Blog.CSDN.net/Poechant
-
相關文章
- Nginx 原始碼完全剖析(11)ngx_spinlockNginx原始碼
- Nginx原始碼完全註釋(6)core/murmurhashNginx原始碼
- Nginx原始碼完全註釋(9)nginx.c: ngx_get_optionsNginx原始碼
- Nginx原始碼完全註釋(8)ngx_errno.cNginx原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】Vector原始碼剖析Java原始碼
- 【Java集合原始碼剖析】HashMap原始碼剖析Java原始碼HashMap
- 【Java集合原始碼剖析】Hashtable原始碼剖析Java原始碼
- 【Java集合原始碼剖析】TreeMap原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】LinkedHashmap原始碼剖析Java原始碼HashMap
- Nginx原始碼完全註釋(5)core/ngx_cpuinfo.cNginx原始碼UI
- 10分鐘剖析Android原始碼——序Android原始碼
- epoll–原始碼剖析原始碼
- HashMap原始碼剖析HashMap原始碼
- Alamofire 原始碼剖析原始碼
- Handler原始碼剖析原始碼
- Kafka 原始碼剖析Kafka原始碼
- TreeMap原始碼剖析原始碼
- SDWebImage原始碼剖析(-)Web原始碼
- Boost原始碼剖析--原始碼
- Spring原始碼剖析9:Spring事務原始碼剖析Spring原始碼
- Nginx原始碼完全註釋(1)ngx_alloc.h / ngx_alloc.cNginx原始碼
- Nginx原始碼完全註釋(7)ngx_palloc.h/ngx_palloc.cNginx原始碼
- Nginx原始碼完全註釋(4)ngx_queue.h / ngx_queue.cNginx原始碼
- Nginx原始碼完全註釋(3)ngx_list.h / ngx_list.cNginx原始碼
- Nginx原始碼完全註釋(2)ngx_array.h / ngx_array.cNginx原始碼
- Flutter 原始碼剖析(一)Flutter原始碼
- 全面剖析 Redux 原始碼Redux原始碼
- vue原始碼剖析(一)Vue原始碼
- Kafka 原始碼剖析(一)Kafka原始碼
- Thread原始碼剖析thread原始碼
- Retrofit 原始碼剖析-深入原始碼
- SDWebImage原始碼剖析(二)Web原始碼
- iOS Aspects原始碼剖析iOS原始碼
- Apache Spark原始碼剖析ApacheSpark原始碼
- 《STL原始碼剖析》-- memory原始碼