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,等了解其具體使用場景時再來分析這一塊。