nginx-rtmp模块内存池剖析

ngx_rtmp_shared.c文件主要是nginx-rtmp的内存池

nginx本身有一套自己的内存池,nginx-rtmp模块根据业务特性音视频又写了一套内存回收的机制

下面主要从nginx-rtmp内存池的内部实现原理以及内存池的使用做介绍分析

提供外部调用的API

  • ngx_rtmp_alloc_shared_buf 申请分配块内存
  • ngx_rtmp_free_shared_chain 内存块回收
  • ngx_rtmp_append_shared_bufs 内存拷贝追加
  • ngx_rtmp_acquire_shared_chain 内存块引用计数加加

内存块申请

ngx_rtmp_alloc_shared_buf主要用来申请块内存,块内存大小是固定大小根据chunk_size配置的,默认是4096

  • 每次申请先从cscf->free链表中取,如果有取一个节点
  • 如果没有,重新申请内存块,内存块buf大小 cscf->chunk_size + NGX_RTMP_MAX_CHUNK_HEADER
  • 给chain的bufpos、start、end位置置位指向实际可以存放数据的位置

关于引用计数可以看这篇 “rtmp模块引用计数

ngx_chain_t *
ngx_rtmp_alloc_shared_buf(ngx_rtmp_core_srv_conf_t *cscf)
{
    u_char                     *p;
    ngx_chain_t                *out;
    ngx_buf_t                  *b;
    size_t                      size;

    /* 先从回收块内存的free链表指针取,有就取一块,没有就重新申请 */
    if (cscf->free) {
        out = cscf->free;
        cscf->free = out->next;

    } else {
        /* 先申请块用以存放音视频帧body的数据块 + rtmp header的大小18字节 */
        size = cscf->chunk_size + NGX_RTMP_MAX_CHUNK_HEADER;

        /* 整个块内存的带下,引用计数占用的四字节 + chain本身结构体 + buf结构体 + size 大小 */
        p = ngx_pcalloc(cscf->pool, NGX_RTMP_REFCOUNT_BYTES
                + sizeof(ngx_chain_t)
                + sizeof(ngx_buf_t)
                + size);
        if (p == NULL) {
            return NULL;
        }
        
        /* 由于引用计数是提前占了四字节,所以chain的位置从四字节后开始取 */
        p += NGX_RTMP_REFCOUNT_BYTES;
        out = (ngx_chain_t *)p;

        p += sizeof(ngx_chain_t);
        out->buf = (ngx_buf_t *)p;

        p += sizeof(ngx_buf_t);
        out->buf->start = p;
        /* 将指针位置buf->start位置指向真正存放数据的位置 */
        out->buf->end = p + size;
    }

    out->next = NULL;
    b = out->buf;
    b->pos = b->last = b->start + NGX_RTMP_MAX_CHUNK_HEADER;
    b->memory = 1;

    /* buffer has refcount =1 when created!  设置当前内存块引用计数值为1 */
    ngx_rtmp_ref_set(out, 1);

    return out;
}

从上面代码分析可知,rtmp模块每次申请都是优先从free链表内存池里面先取,不够再去申请。

内存块回收

ngx_rtmp_free_shared_chain对于chain块内存回收的时候,并不是真正意义上的free内存,而是放到链表当中下次循环使用,这样的好处减少频繁内存申请释放

void
ngx_rtmp_free_shared_chain(ngx_rtmp_core_srv_conf_t *cscf, ngx_chain_t *in)
{
    ngx_chain_t        *cl;

    /* 释放的时候,优先判断一下引用计数是否为0,如果不为0 当前并不释放,因为还有其他的地方再用 */
    if (ngx_rtmp_ref_put(in)) {
        return;
    }

    /* 引用计数为0,将当前内存块放到链表的结尾 */
    for (cl = in; ; cl = cl->next) {
        if (cl->next == NULL) {
            cl->next = cscf->free;
            cscf->free = in;
            return;
        }
    }
}

free内存的时候,注意以下几点

  • 1)引用计数为0,才开始释放内存
  • 2)释放内存并不是真正的调用系统调用free操作,而是将当前内存块通过尾插法的方式放到cscf->free链表当中

内存追加

ngx_rtmp_append_shared_bufs 主要是内存追加,将in里面的数据放到head的后面

ngx_chain_t *
ngx_rtmp_append_shared_bufs(ngx_rtmp_core_srv_conf_t *cscf,
        ngx_chain_t *head, ngx_chain_t *in)
{
    ngx_chain_t                    *l, **ll;
    u_char                         *p;
    size_t                          size;

    ll = &head;
    p = in->buf->pos;
    l = head;

    /* 先将指针指向head链表最后的位置 */
    if (l) {
        for(; l->next; l = l->next);
        ll = &l->next;
    }

    for ( ;; ) {
        /* 新分配一块内存块l */
        if (l == NULL || l->buf->last == l->buf->end) {
            l = ngx_rtmp_alloc_shared_buf(cscf);
            if (l == NULL || l->buf == NULL) {
                break;
            }

            *ll = l;
            ll = &l->next;
        }

        /* 将内存块in里面的内容拷贝到l当中,in有可能后面next还有数据所以是循环遍历 */
        while (l->buf->end - l->buf->last >= in->buf->last - p) {
            l->buf->last = ngx_cpymem(l->buf->last, p,
                    in->buf->last - p);
            in = in->next;
            if (in == NULL) {
                goto done;
            }
            p = in->buf->pos;
        }

        /* 如果后面l 后面内存空间不够用,将剩余可用的先拷贝进去然后再申请新的内存块再拷贝 */
        size = l->buf->end - l->buf->last;
        l->buf->last = ngx_cpymem(l->buf->last, p, size);
        p += size;
    }

done:
    *ll = NULL;

    /* in里面的数据拷贝追加完之后,再返回链表头 head */
    return head;
}

总结:
内存循环利用的原理其实很简单:

  • 1)弄一个cscf->free链表用来回收内存块,下次需要重新分配先从free链表取,减少内存块频繁的申请和释放
  • 2)引用计数的应用,引用计数C++里面应用比较广泛,C语言这种用法确实很巧妙。以后自己设计也可以这么用

猜你喜欢

转载自blog.csdn.net/wu5215080/article/details/90758992