`
Poechant
  • 浏览: 227362 次
博客专栏
Bebe66e7-3a30-3fc9-aeea-cfa3b474b591
Nginx高性能Web服务...
浏览量:24231
5738817b-23a1-3a32-86de-632d7da73b1e
Cumulus实时媒体服务...
浏览量:22027
社区版块
存档分类
最新评论

Nginx 源码完全剖析(10)ngx_radix_tree

阅读更多

Nginx 源码完全剖析(10)ngx_radix_tree

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

-

分享到:
评论

相关推荐

    ngx_http_proxy_connect_module.zip

    ngx_http_proxy_connect_module是一个针对Nginx服务器的第三方模块,主要功能是支持HTTP代理的"CONNECT"方法。在默认情况下,Nginx仅处理HTTP和HTTPS请求,但不支持通过HTTP代理进行TCP连接,比如SSL/TLS隧道。这个...

    nginx缓存清除插件ngx_cache_purge.zip

    ngx_cache_purge 是 nginx 模块,此模块可以清理 nginx 的 FastCGI、proxy、 SCGI 和 uWSGI 的缓存。配置指令(相同位置语法)fastcgi_cache_purgesyntax: fastcgi_cache_purge on|off|&lt;method&gt; [from all|&lt;ip&gt; [.....

    ngx_devel_kit-0.3.0

    ngx_devel_kit(通常缩写为 NDK)是一个针对Nginx的模块开发工具集,它为构建自定义Nginx模块提供了便利。在Nginx生态系统中,NDK是一个重要的扩展工具,允许开发者利用C语言直接操作Nginx的内部结构,以实现更高级...

    ngx_lua_module-windows-1.1.2.0

    ngx_lua_module是一款强大的扩展模块,专为Nginx服务器设计,允许在Nginx配置文件中直接嵌入Lua脚本,极大地增强了Nginx的功能和灵活性。这个"ngx_lua_module-windows-1.1.2.0"是该模块的一个Windows版本,适应于...

    nginx限制连接数ngx_http_limit_conn_module模块1

    【Nginx 限制连接数 ngx_http_limit_conn_module 模块详解】 在互联网服务中,服务器经常面临流量异常、负载过大的情况,尤其在遭受大流量恶意攻击时,带宽的浪费、服务器压力的增大都会对业务造成严重影响。为了...

    lua-upstream-nginx-module, Nginx C 模块将Lua向ngx_lua公开,用于 Nginx upstreams.zip

    lua-upstream-nginx-module, Nginx C 模块将Lua向ngx_lua公开,用于 Nginx upstreams 电子邮件名称ngx_http_lua_upstream - Nginx MODULE,用于向 Nginx upstreams公开Lua到 ngx_lua目录NAME状态概要说明函数get_...

    Nginx模块ngx_req_status

    ngx_req_status用来展示nginx请求状态信息,类似于apache的status,nginx自带的模块只能显示连接数等等信息,我们并不能知道到底有哪些请求、以及各url域名所消耗的带宽是多少。ngx_req_status提供了这些功能. 1、按...

    Nginx的SQLite模块ngx_sqlite.zip

    ngx_sqlite 是嵌入 sqlite 数据库的 nginx 模块。通过强大的nginx server,可以使用http协议访问sqlite数据库。环境- sqlite 3- nginx-1.6.3 安装```sh $ git clone https://github.com/rryqszq4/ngx_sqlite.git...

    使用nginx作为http/https正向代理ipm包,包含ngx_http_proxy_connect_module 模块,第三方图片代理,带有缓存

    使用nginx作为http/https正向代理ipm包,包含ngx_http_proxy_connect_module 模块,附带了第三方图片...下载后执行:rpm -ivh nginx-1.12.2-1.el7_4.ngx.x86_64.rpm 打包教程:https://www.jianshu.com/p/cd1c04c31534

    ngx_cache_purge_2.4.2.tar.gz

    这个版本2.4.2的压缩包包含了ngx_cache_purge模块的所有源代码及相关文件,以便开发者在自己的Nginx环境中集成和使用。 Nginx是一款高性能、轻量级的Web服务器和反向代理服务器,广泛应用于互联网服务。其内置的...

    ngx_devel_kit-0.2.19.tar.gz

    ngx_devel_kit是Nginx的一个重要扩展模块,它为Nginx提供了一系列的开发工具,方便开发者在Nginx上构建自定义模块。这个0.2.19版本的压缩包“ngx_devel_kit-0.2.19.tar.gz”包含了用于编译和开发Nginx模块所需的源...

    ngx_http_dav_ext_module.so

    ngx_http_dav_ext_module.so centos7 nginx 1.18 可以作为模块加载

    ngx_stream_module.so

    nginx1.20.2

    ngx_stream_ssl_preread_module调研.docx

    NGX_STREAM_SSL_PREREAD_MODULE 模块调研 NGX_STREAM_SSL_PREREAD_MODULE 是一个基于流媒体(Stream)实现的 SSL/TLS 协议 ClientHello 消息提取模块,主要用于提取 SNI(Server Name Indicate)或者 ALPN...

    ngx_lua_waf-master.zip_lua_nearly11h_nginx_ngx_lua_waf

    ngx_lua_waf是一个基于Nginx的Web应用防火墙,其核心是利用了Nginx的ngx_lua模块,通过Lua脚本实现灵活且强大的安全防护功能。ngx_lua_waf项目名称中的“master”通常指的是该项目的主分支或最新版本。"nearly11h...

    nginx的ngx_queue_t的使用demo

    同时,理解ngx_queue_t的实现方式也有助于提升对Nginx源码的理解,对于那些希望定制Nginx或者进行性能优化的开发者来说尤其重要。 总的来说,这个资源提供了一个学习和实践Nginx核心数据结构ngx_queue_t的良好机会...

    nginx流媒体安装包(nginx_mod_h264_streaming,yamdi)

    【Nginx流媒体服务器构建与Yamdi工具详解】 在现代互联网中,视频流服务已经成为不可或缺的一部分。Nginx,作为一个高性能的Web服务器和反向代理服务器,因其高效的并发处理能力和轻量级的特性,常被用于搭建流媒体...

    Nginx模块ngx_realtime_request

    ngx_realtime_request是nginx用来统计虚拟主机流量的模块, 首先和大家说下这个模块是基于域名的,将会记录这个域名的请求量、发送字节、返回http状态码的数量,特性如下: 1、基于域名记录 2、记录请求数据量 3、...

    ngx_http_consistent_hash-master.zip

    "nginx" 表明这个话题与 Nginx 服务器软件有关,而 "一致性哈希模块" 暗示我们讨论的是 Nginx 中用于处理一致性哈希功能的扩展模块。 **压缩包子文件列表:** 由于未提供具体的子文件列表,我们假设该压缩包包含了...

Global site tag (gtag.js) - Google Analytics