Swoole源码学习记录(三)——三种MemoryPool(下)

本文详细介绍了Swoole 1.7.4-stable版本中的RingBuffer,它是内存池的一种实现,具有循环数组特性。通过分析`swRingBuffer_head`和`swRingBuffer`结构体,展示了RingBuffer如何管理和回收内存。RingBuffer的`alloc`、`destroy`、`free`和`collect`函数用于操作内存池,其中`collect`函数负责回收未使用的内存块,确保内存高效利用。
摘要由CSDN通过智能技术生成

swoole版本:1.7.4-stable

Github地址: https://github.com/LinkedDestiny/swoole-src-analysis

接下来是RingBuffer。这相当于一个循环数组,每一次申请的一块内存在该数组中占据一个位置,这些内存块是可以不等长的,因此每个内存块需要有一个记录其长度的变量。这里贴出swRingBuffer_head结构体的代码:

typedef struct _swRingBuffer_item
{
    volatileuint32_t lock;
    volatileuint32_t length;
} swRingBuffer_head;

每一个结构体代表一个RingBuffer中的内存块,其中lock变量标记该内存块是否被占用,length变量标记该内存块的长度。

接着是swRingBuffer结构体的声明:

typedef struct _swRingBuffer
{
    uint8_t shared;                 //可共享
    size_t size;                    //内存池大小
    volatile off_talloc_offset;    // 分配内存的起始长度
    volatile off_tcollect_offset;  // 可用内存的终止长度
    volatileuint32_t free_n;       // 有多少个内存块待回收
    void *memory;                   // 内存池的起始地址
} swRingBuffer;

每一个结构体代表一个RingBuffer内存池。这里先要说明一下RingBuffer的三个变量:alloc_offset,collect_offset,free_n。

alloc_offset变量是分配内存的起始地址,代表的是RingBuffer现有的可用空间的起始地址;collect_offset变量是分配内存的终止地址,代表的是RingBuffer现有的可用空间的结束地址。为了方便理解,大家可以想象一下循环队列,alloc_offset和collect_offset就是标记队头和队尾的标记,每一次分配内存就相当于入队,每一次释放内存就相当于出队。

而free_n变量是用于标记当前还有多少个已释放的内存块待回收。这是因为RingBuffer采用的是连续分配,可能会存在一些已经被free的内存块夹在两个没有free的内存块中间,没有被立即回收,就需要一个变量去通知内存池回收这些内存。。

    RingBuffer的创建函数为swRingBuffer_new,其声明在swoole.h文件的514 – 517行。入下:

/**
 * RingBuffer, Inorder for malloc / free
 */
swMemoryPool *swRingBuffer_new(size_t size, uint8_tshared);

该函数的具体定义在RingBuffer.c中,创建过程与FixedPool基本类似,就不再额外分析,大家自行阅读源码即可。

    和FixedPool类似,RingBuffer也拥有4个函数用于操作内存池,其函数声明如下:

static void swRingBuffer_destory(swMemoryPool *pool);
static sw_inline void swRingBuffer_collect(swRingBuffer*object);
static void* swRingBuffer_alloc(swMemoryPool *pool,uint32_t size);
static void swRingBuffer_free(swMemoryPool *pool, void*ptr);

其中alloc、destroy、free三个函数的功能很明确,swRingBuffer_collect函数用于回收已经不被占用的内存。这里着重分析alloc函数和collect函数。

首先是collect函数。在发现内存池剩余不足或分配内存结束后,RingBuffer都会调用collect函数去回收已经没有被占用的内存。其核心代码如下:

  

  for(i = 0;i<SW_RINGBUFFER_COLLECT_N; i++)
    {
        item =(swRingBuffer_head *) (object->memory + object->collect_offset);
 
        swTraceLog(SW_TRACE_MEMORY,"collect_offset=%d, item_length=%d, lock=%d",object->collect_offset, item->length, item->lock);
 
        //cancollect
        if(item->lock == 0)
        {
            object->collect_offset+= (sizeof(swRingBuffer_head) + item->length);
            if(object->free_n > 0)
            {
                object->free_n--;
            }
            if(object->collect_offset >= object->size)
            {
                object->collect_offset= 0;
            }
        }
        else
        {
            break;
        }
    }

源码解释:每一次循环,都会获取当前collect_offset指向的地址代表的内存块,并获取其swRingBuffer_head结构,如果该内存块已经被free,则将collect_offset标记后移该内存块的长度,回收该内存。如果发现collect_offset超出了内存池大小,则将collect_offset移到内存池头部。

alloc函数太长,在此不贴出源码,只写出伪代码供分析:

start_alloc:
if( alloc_offset < collect_offset ) // 起始地址在终止地址左侧
{
    head_alloc:
    计算剩余内存大小
   
    if( 内存足够 )
        gotodo_alloc
    else if( 内存不足且已经回收过内存)
        return NULL;
    else
    {
        try_collect = 1;
        调用collect
        goto start_alloc
    }
}
else // 起始地址在终止地址右侧(终止地址被移动到了首部)
{
    计算从alloc_offset到内存池尾部的剩余内存大小
    if( 内存足够 )
        gotodo_alloc
    else
    {
    标记尾部剩余内存为可回收状态
    将alloc_offset移动到首部
    gotohead_alloc
}
}
do_alloc:
实际分配内存块并设置属性,移动alloc_offset标记
如果free_n大于0,则回收内存。

最后是MemoryGlobal。MemoryGlobal是一个比较特殊的内存池。说实话我没有看懂它的作用,所以我决定先暂时跳过MemoryGlobal,等了解其具体使用场景时再来分析这一块。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值