前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nginx(10):数据类型 与 数据结构之 ngx_array

Nginx(10):数据类型 与 数据结构之 ngx_array

作者头像
看、未来
发布2021-10-09 11:54:20
2560
发布2021-10-09 11:54:20
举报
文章被收录于专栏:CSDN搜“看,未来”
请添加图片描述
请添加图片描述

文章目录

基本数据类型封装

代码语言:javascript
复制
typedef intptr_t        ngx_int_t;
typedef uintptr_t       ngx_uint_t;

数据结构

由于Nginx对内存分配比 较“吝啬”(只有保证低内存消耗,才可能实现十万甚至百万级别的同时并发连接数),所以这些Nginx数据结构天生都是尽可能少占用内存。

字符串

代码语言:javascript
复制
typedef struct {
    size_t      len;		//字符串的有效长度
    u_char     *data;		//指向字符串起始地址
} ngx_str_t;

我发现它这个源码里面注释是真的少哈。。。

ngx_str_t的data成员指向的并不是普通的字符串,因为这段字符串未必会以’\0’作 为结尾,所以使用时必须根据长度len来使用data成员。

那到这里有的人就想说了:那这不是更耗内存了吗?怎么还反而少占用内存?

我也想知道为什么呢。有疑惑先留着,看到后面自然就有答案了。

跟字符串相关的方法都要带上长度,不然容易出现越界。


空间配置器

代码语言:javascript
复制
typedef struct {
    u_char               *last;	//当前内存池分配到此处,即下一次分配从此处开始 
    u_char               *end;	//内存池结束位置
    ngx_pool_t           *next;	//内存池里面有很多块内存,这些内存块就是通过该指针连成链表的 
    ngx_uint_t            failed;	//内存池分配失败次数 
} ngx_pool_data_t;


struct ngx_pool_s {
    ngx_pool_data_t       d;		//内存池的数据块 
    size_t                max;	//内存池数据块的最大值
    ngx_pool_t           *current;	//指向当前内存池
    ngx_chain_t          *chain;		//该指针挂接一个ngx_chain_t结构,下篇有,但是这个更重要,先放
    ngx_pool_large_t     *large;		//大块内存链表,即分配空间超过max的内存
    ngx_pool_cleanup_t   *cleanup;	//释放内存池的callback
    ngx_log_t            *log;		//日志信息
};

数组

代码语言:javascript
复制
typedef struct {
    void        *elts;		//指向数组的起始地址
    ngx_uint_t   nelts;		//表示数组中已经使用元素数量
    size_t       size;		//限制每个数组元素占用空间大小,也就是用户要存储的一个数据所 占用的字节数必须小于或等于size。
    ngx_uint_t   nalloc;	//最多可存储数据数
    ngx_pool_t  *pool;		//管理内存分配的内存池对象
} ngx_array_t;
代码语言:javascript
复制
//就字面意思呗,也没啥好看的,就普通的初始化空间。
ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)
{
    ngx_array_t *a;

    a = ngx_palloc(p, sizeof(ngx_array_t));
    if (a == NULL) {
        return NULL;
    }

    if (ngx_array_init(a, p, n, size) != NGX_OK) {
        return NULL;
    }

    return a;
}

static ngx_inline ngx_int_t 
ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
    /*
     * set "array->nelts" before "array->elts", otherwise MSVC thinks
     * that "array->nelts" may be used without having been initialized
     */

    array->nelts = 0;
    array->size = size;
    array->nalloc = n;
    array->pool = pool;

    array->elts = ngx_palloc(pool, n * size);		//在这里分配空间
    if (array->elts == NULL) {
        return NGX_ERROR;
    }

    return NGX_OK;
}

创建数组后内存池的物理结构图如下:

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
void ngx_array_destroy(ngx_array_t *a)
{
    ngx_pool_t  *p;

    p = a->pool;

    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {	//先销毁数组数据区
        p->d.last -= a->size * a->nalloc;
    }

    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {	//接着销毁数组头
        p->d.last = (u_char *) a;
    }
}

实际的添加操作并不在这两个函数中完成,例如ngx_array_push返回可以在该数组数据区中添加这个元素的位置,ngx_array_push_n则返回可以在该数组数据区中添加n个元素的起始位置,而添加操作即在获得添加位置之后进行

代码语言:javascript
复制
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;	//这是干嘛,有点猛啊,哦,size是局部变量

        p = a->pool;

        if ((u_char *) a->elts + size == p->d.last
            && p->d.last + a->size <= p->d.end)		//还没弹尽粮绝呢?那也不对啊,怎么是小等于,那不就会山穷水尽吗
        {																			//除非,这个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);	//否则,扩展数组数据区为原来的2倍
            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;	//返回该末尾指针,即下一个元素应该存放的位置
}


void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n)
{
    void        *elt, *new;
    size_t       size;
    ngx_uint_t   nalloc;
    ngx_pool_t  *p;

    size = n * a->size;

    if (a->nelts + n > a->nalloc) {

        /* the array is full */

        p = a->pool;

        if ((u_char *) a->elts + a->size * a->nalloc == p->d.last
            && p->d.last + size <= p->d.end)
        {
            /*
             * the array allocation is the last in the pool
             * and there is space for new allocation
             */

            p->d.last += size;
            a->nalloc += n;

        } else {
            /* allocate a new array */

            nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);

            new = ngx_palloc(p, nalloc * a->size);
            if (new == NULL) {
                return NULL;
            }

            ngx_memcpy(new, a->elts, a->nelts * a->size);
            a->elts = new;
            a->nalloc = nalloc;
        }
    }

    elt = (u_char *) a->elts + a->size * a->nelts;
    a->nelts += n;

    return elt;
}

向数组中添加元素实际上也是在修该内存池的last指针(若数组数据区满)及数组头信息,即使数组满了,需要扩展数据区内容,也只需要内存拷贝完成,并不需要数据的移动操作,这个效率也是相当高的。


太多了慢慢来,一次太多 hold 不住。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/09/27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 基本数据类型封装
  • 数据结构
    • 字符串
      • 空间配置器
        • 数组
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档