uc/os-II的記憶體改進與實現TLSF演算法的詳解,移植實現(二)
上一節講到了TLSF的資料結構,下面繼續哈。
TLSF用兩個層次的分類對不同尺寸的記憶體塊進行分類。第一層次的類別目錄為2n,n為4,5,……,31的整數,稱為FLI(First-level Segregated Fit)。每一個FLI類別又根據第二層的SLI細分為2SLI個子類別。第二層的每個類別,都對應一條屬於該類別尺寸範圍內的記憶體塊連結串列。為了加快分配與合併記憶體塊的速度,連結串列是不排序的。所有的連結串列頭指標用陣列元素尺寸為32位的二維陣列儲存起來。各個類別所表示的記憶體塊尺寸範圍可參見圖1。第一層次、第二層次都使用點陣圖指示該類別有無空閒記憶體塊,有則該類別對應的位為1,否則為0。詳情看上圖哈。圖裡說的很明顯了。
1.1 TLSF資料結構
TLSF 將管理記憶體塊所需要的資訊嵌入到每一個模組中(無論模組空閒與否)並將聯絡模組的指標放入到兩個連結串列:
帶有一定大小記憶體塊的連結串列和由實體地址管理的連結串列。這種資料結構稱為“頭部結構”。由於申請記憶體塊的大小一般為4 的倍數,所以兩個最低有效位用來表示記憶體的狀態:記憶體塊是否空閒(F 位),記憶體塊是否是記憶體池中的最後一個(T 位)。我們利用公式1 來確定所申請記憶體處於的連結串列位置[3]:
例如,假設SLI=4 且size=460,則第一級目錄中的f=8,第二級目錄中的s=12。
好,TLSF的資料結構就講這麼多,下面詳細講解下TLSF演算法。
2 .首先說下用到的結構體。
上一節說到了TLSF的結構體,它裡面還定義了一些結構體。下面看一下定義。
2.1 area_info_t 結構體
結構體area—info—t用來連結各個不相鄰的記憶體區,
其結構如下:
/*由於連線多個記憶體區,*/
typedef struct area_info_struct {
bhdr_t *end; /*指向末記憶體塊,,記憶體區(池)最後一塊,低2字*/
struct area_info_struct *next; /*指向下一個記憶體區,新增的記憶體*/
} area_info_t;
2.2 bhdr_t 結構體結構體bhdr_t儲存各個空閒連結串列的表頭,如果此連結串列中無空閒記憶體塊,則為null。
結構體如下所示:
typedef struct bhdr_struct {
/* This pointer is just valid if the first bit of size is set */
struct bhdr_struct *prev_hdr; /*指向前一個實體記憶體塊*/
/* The size is stored in bytes ,size之後歸此bhdr_t控制塊管理的記憶體塊大小*/
size_t size; /* bit 0 indicates whether the block is used and */
/* bit 1 allows to know whether the previous block is free */
/*記憶體塊的大小,不包括prev hdr與size的大小*/
union {
struct free_ptr_struct free_ptr;
u8_t buffer[1]; /*sizeof(struct free_ptr_struct)]; ,注意讀取buffer的值等於讀取其地址
即此聯合體的首地址,便於讀取ptr的首地址。 */
} ptr;
} bhdr_t;
而結構體struct free_ptr_structt用來連結一連結串列中的各個空閒記憶體塊,
結構如下:
typedef struct free_ptr_struct {
struct bhdr_struct *prev;/*連結邏輯上的前一個記憶體塊*/
struct bhdr_struct *next;<span style="font-family: Arial, Helvetica, sans-serif;">/*連結邏輯上的後一個記憶體塊*/</span>
} free_ptr_t;
好,結構體就有這些。等到我們移植的時候也是移植這些結構體。
下面說下TLSF演算法用到的一些函式。
3. 重要函式!
TLSF演算法主要包括:記憶體區的初始化函式imtmemory_pool()、記憶體區銷燬函式destroy_memory_pool()、增加記憶體區函式add_new_area(),以及記憶體分配相關的函式tlsf_malloc()、tlsf_free()、tlsf_realloc()、tlsf_calloc()等,還有一些自己帶的列印記憶體塊狀態資訊的列印函式等除錯函式。
3.1 記憶體池初始化函式init_memory_pool()
此函式用來初始化一塊大的記憶體區,為結構體tlsf賦值(記憶體區首地址的N個位元組),並通過呼叫函式process_area對剩下的記憶體區進行處理,處理後的記憶體如圖2所示。之後,把記憶體塊b釋放掉,得到初始記憶體塊b,這也是整個記憶體區所管理的動態記憶體大小。
3.2 記憶體池銷燬函式destroy_memory_pool()
/* 記憶體池銷燬函式*/
/******************************************************************/
void destroy_memory_pool(void *mem_pool)
{
/******************************************************************/
tlsf_t *tlsf = (tlsf_t *) mem_pool;
tlsf->tlsf_signature = 0; /* 用來表示記憶體區銷燬*/
TLSF_DESTROY_LOCK(&tlsf->lock); /* 作業系統函式相關,或自定義函式*/
}
3.3記憶體分配函式tlsf_malloc()
/* 函式功能:tlsf記憶體分配函式
形參: size 所需記憶體的大小
返回: viod * (無符號指標)。分配成功後,返回記憶體塊的指標ret;分配失敗返回NULL。
*/
/******************************************************************/
void *tlsf_malloc(size_t size)
{
/******************************************************************/
void *ret;
#if USE_MMAP || USE_SBRK
if (!mp) { /* 如果分配記憶體塊時,沒有初始化一個記憶體區,可以使用以下函式得到一個記憶體區,並初始化此記憶體區*/
size_t area_size;
void *area;
area_size = sizeof(tlsf_t) + BHDR_OVERHEAD * 8; /* Just a safety constant */
area_size = (area_size > DEFAULT_AREA_SIZE) ? area_size : DEFAULT_AREA_SIZE;
area = get_new_area(&area_size);
if (area == ((void *) ~0))
return NULL; /* Not enough system memory */
init_memory_pool(area_size, area);
}
#endif
TLSF_ACQUIRE_LOCK(&((tlsf_t *)mp)->lock); /*獲取上鎖,與作業系統有關*/
ret = malloc_ex(size, mp);
TLSF_RELEASE_LOCK(&((tlsf_t *)mp)->lock); /*獲取解鎖,與作業系統有關*/
return ret;
}
此函式中,主要是通過內部記憶體分配函式malloc_ex()來實現的,
其流程如圖3所示。
3.4 記憶體釋放函式tlsf_free()
記憶體釋放的主要工作在函式free_ex()中實現,主要是判斷釋放記憶體塊的前後記憶體塊是否也是空閒的,如果是空閒記憶體塊,兩塊記憶體塊合併為一個大的記憶體塊,並根據記憶體大小加入相應的空閒連結串列中,並調整bit位。
/* 函式功能:tlsf記憶體釋放函式
形參: ptr 所需記憶體的首地址指標
返回: viod 無返回
*/
/******************************************************************/
void tlsf_free(void *ptr)
{
/******************************************************************/
TLSF_ACQUIRE_LOCK(&((tlsf_t *)mp)->lock); /*上鎖,與作業系統有關*/
free_ex(ptr, mp);
TLSF_RELEASE_LOCK(&((tlsf_t *)mp)->lock); /*解鎖,與作業系統有關*/
}
/* 函式功能:釋放ftr所在的記憶體塊,並根據情況合併前後記憶體塊,更新相應bitmap標誌位
形參: ptr 釋放記憶體指標; men_pool 記憶體池的首地址
返回: viod * (無符號指標)。分配成功後,返回記憶體塊的指標ret;分配失敗返回NULL。
*/
/* */
/******************************************************************/
void free_ex(void *ptr, void *mem_pool)
{
/******************************************************************/
tlsf_t *tlsf = (tlsf_t *) mem_pool;
bhdr_t *b, *tmp_b;
int fl = 0, sl = 0;
if (!ptr) { /*ptr為NULL,直接返回*/
return;
}
b = (bhdr_t *) ((char *) ptr - BHDR_OVERHEAD);
b->size |= FREE_BLOCK; /* 所釋放記憶體塊狀態更新(size後兩位更新)*/
TLSF_REMOVE_SIZE(tlsf, b); /* #if TLSF_STATISTIC */
b->ptr.free_ptr.prev = NULL;
b->ptr.free_ptr.next = NULL;
tmp_b = GET_NEXT_BLOCK(b->ptr.buffer, b->size & BLOCK_SIZE); /* 得到b塊後面的相鄰物理塊指標*/
if (tmp_b->size & FREE_BLOCK) { /* b塊後面塊是free的?其後記憶體塊free則合併記憶體*/
MAPPING_INSERT(tmp_b->size & BLOCK_SIZE, &fl, &sl); /* 根據tmp_b大小求出一級與二級索引值*/
EXTRACT_BLOCK(tmp_b, tlsf, fl, sl); /* 提取記憶體塊,並根據記憶體塊在連結串列中的位置調整空閒連結串列與點陣圖標誌位*/
b->size += (tmp_b->size & BLOCK_SIZE) + BHDR_OVERHEAD; /* 把b(ptr)後面的記憶體塊合併到b記憶體塊中,size更新*/
}
if (b->size & PREV_FREE) { /* b塊前一塊free?free則與前面的記憶體塊合併*/
tmp_b = b->prev_hdr; /* 得到b塊前1物理塊 */
MAPPING_INSERT(tmp_b->size & BLOCK_SIZE, &fl, &sl);
EXTRACT_BLOCK(tmp_b, tlsf, fl, sl);
tmp_b->size += (b->size & BLOCK_SIZE) + BHDR_OVERHEAD;
b = tmp_b; /* 更新b指標的值,即b指向合併後的記憶體塊地址*/
}
MAPPING_INSERT(b->size & BLOCK_SIZE, &fl, &sl); /**/
INSERT_BLOCK(b, tlsf, fl, sl); /* 把釋放的記憶體塊插入相應連結串列的表頭*/
tmp_b = GET_NEXT_BLOCK(b->ptr.buffer, b->size & BLOCK_SIZE);
tmp_b->size |= PREV_FREE; /* 更新後一塊的資訊,以表示釋放的記憶體塊空閒的*/
tmp_b->prev_hdr = b; /* 更新後一塊記憶體塊的物理塊prev_hdr*/
}
除了這些函式外,還有很多的函式用到。這些需要大家看原始碼了哦。不過重要的函式就這幾個哈。
下節講講怎麼講這個演算法移植到uc/os上哈!
未完待續!!