本篇關鍵詞:池頭、池體、節頭、節塊
記憶體管理相關篇為:
相比動態分配,靜態記憶體池的分配就是個小弟弟,非常的簡單,兩個結構體 + 一張圖 就能說明白。
typedef struct {//靜態記憶體池資訊結構體
UINT32 uwBlkSize; /**< Block size | 塊大小*/
UINT32 uwBlkNum; /**< Block number | 塊數量*/
UINT32 uwBlkCnt; /**< The number of allocated blocks | 已經被分配的塊數量*/
LOS_MEMBOX_NODE stFreeList; /**< Free list | 空閒連結串列*/
} LOS_MEMBOX_INFO;
typedef struct tagMEMBOX_NODE { //記憶體池中空閒節點的結構,是個單向的連結串列
struct tagMEMBOX_NODE *pstNext; /**< Free node's pointer to the next node in a memory pool | 指向記憶體池中下一個空閒節點的指標*/
} LOS_MEMBOX_NODE;
下圖來源於官網
解讀
LOS_MEMBOX_INFO
(池頭) + [LOS_MEMBOX_NODE
(節頭) + data
(節體)] + ... + [LOS_MEMBOX_NODE
(節頭) + data
(節體)] ,在虛擬地址上它們是連在一起的。stFreeList
將所有空閒節塊連結到一起,分配記憶體根本不需要遍歷,stFreeList
指向的下一個不為null
代表還有空閒節塊。pstNext
指標,簡單但足以。因程式碼量不大,但很精彩,看這種程式碼是種享受,本篇詳細列出靜態記憶體程式碼層面的實現,關鍵處已新增註釋。
///初始化一個靜態記憶體池,根據入參設定其起始地址、總大小及每個記憶體塊大小
LITE_OS_SEC_TEXT_INIT UINT32 LOS_MemboxInit(VOID *pool, UINT32 poolSize, UINT32 blkSize)
{
LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;//在記憶體起始處放置控制頭
LOS_MEMBOX_NODE *node = NULL;
//...
UINT32 index;
UINT32 intSave;
MEMBOX_LOCK(intSave);
boxInfo->uwBlkSize = LOS_MEMBOX_ALIGNED(blkSize + OS_MEMBOX_NODE_HEAD_SIZE); //節塊總大小(節頭+節體)
boxInfo->uwBlkNum = (poolSize - sizeof(LOS_MEMBOX_INFO)) / boxInfo->uwBlkSize;//總節塊數量
boxInfo->uwBlkCnt = 0; //已分配的數量
if (boxInfo->uwBlkNum == 0) {//只有0塊的情況
MEMBOX_UNLOCK(intSave);
return LOS_NOK;
}
node = (LOS_MEMBOX_NODE *)(boxInfo + 1);//去除池頭,找到第一個節塊位置
boxInfo->stFreeList.pstNext = node;//池頭空閒連結串列指向第一個節塊
for (index = 0; index < boxInfo->uwBlkNum - 1; ++index) {//切割節塊,掛入空閒連結串列
node->pstNext = OS_MEMBOX_NEXT(node, boxInfo->uwBlkSize);//按塊大小切割好,統一由pstNext指向
node = node->pstNext;//node儲存了下一個節點的地址資訊
}
node->pstNext = NULL;//最後一個為null
MEMBOX_UNLOCK(intSave);
return LOS_OK;
}
///從指定的靜態記憶體池中申請一塊靜態記憶體塊,整個核心原始碼只有 OsSwtmrScan中用到了靜態記憶體.
LITE_OS_SEC_TEXT VOID *LOS_MemboxAlloc(VOID *pool)
{
LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
LOS_MEMBOX_NODE *node = NULL;
LOS_MEMBOX_NODE *nodeTmp = NULL;
UINT32 intSave;
if (pool == NULL) {
return NULL;
}
MEMBOX_LOCK(intSave);
node = &(boxInfo->stFreeList);//拿到空閒單連結串列
if (node->pstNext != NULL) {//不需要遍歷連結串列,因為這是空閒連結串列
nodeTmp = node->pstNext;//先記錄要使用的節點
node->pstNext = nodeTmp->pstNext;//不再空閒了,把節點摘出去了.
OS_MEMBOX_SET_MAGIC(nodeTmp);//為已使用的節塊設定魔法數位
boxInfo->uwBlkCnt++;//已使用塊數增加
}
MEMBOX_UNLOCK(intSave);
return (nodeTmp == NULL) ? NULL : OS_MEMBOX_USER_ADDR(nodeTmp);//返回可用的虛擬地址
}
/// 釋放指定的一塊靜態記憶體塊
LITE_OS_SEC_TEXT UINT32 LOS_MemboxFree(VOID *pool, VOID *box)
{
LOS_MEMBOX_INFO *boxInfo = (LOS_MEMBOX_INFO *)pool;
UINT32 ret = LOS_NOK;
UINT32 intSave;
if ((pool == NULL) || (box == NULL)) {
return LOS_NOK;
}
MEMBOX_LOCK(intSave);
do {
LOS_MEMBOX_NODE *node = OS_MEMBOX_NODE_ADDR(box);//通過節體獲取節塊首地址
if (OsCheckBoxMem(boxInfo, node) != LOS_OK) {
break;
}
node->pstNext = boxInfo->stFreeList.pstNext;//節塊指向空閒連結串列表頭
boxInfo->stFreeList.pstNext = node;//空閒連結串列表頭反指向它,意味節塊排到第一,下次申請將首個分配它
boxInfo->uwBlkCnt--;//已經使用的記憶體塊減一
ret = LOS_OK;
} while (0);//將被編譯時優化
MEMBOX_UNLOCK(intSave);
return ret;
}
鴻蒙核心目前只有軟時鐘處理使用了靜態記憶體池,直接上程式碼
///軟時鐘初始化 ,注意函數在多CPU情況下會執行多次
STATIC UINT32 SwtmrBaseInit(VOID)
{
UINT32 ret;
UINT32 size = sizeof(SWTMR_CTRL_S) * LOSCFG_BASE_CORE_SWTMR_LIMIT;
SWTMR_CTRL_S *swtmr = (SWTMR_CTRL_S *)LOS_MemAlloc(m_aucSysMem0, size); /* system resident resource */
if (swtmr == NULL) {
return LOS_ERRNO_SWTMR_NO_MEMORY;
}
(VOID)memset_s(swtmr, size, 0, size);//清0
g_swtmrCBArray = swtmr;//軟時鐘
LOS_ListInit(&g_swtmrFreeList);//初始化空閒連結串列
for (UINT16 index = 0; index < LOSCFG_BASE_CORE_SWTMR_LIMIT; index++, swtmr++) {
swtmr->usTimerID = index;//按順序賦值
LOS_ListTailInsert(&g_swtmrFreeList, &swtmr->stSortList.sortLinkNode);//通過sortLinkNode將節點掛到空閒連結串列
}
//想要用靜態記憶體池管理,就必須要使用LOS_MEMBOX_SIZE來計算申請的記憶體大小,因為需要點字首記憶體承載頭部資訊.
size = LOS_MEMBOX_SIZE(sizeof(SwtmrHandlerItem), OS_SWTMR_HANDLE_QUEUE_SIZE);//規劃一片記憶體區域作為軟時鐘處理常式的靜態記憶體池。
g_swtmrHandlerPool = (UINT8 *)LOS_MemAlloc(m_aucSysMem1, size); /* system resident resource */
if (g_swtmrHandlerPool == NULL) {
return LOS_ERRNO_SWTMR_NO_MEMORY;
}
ret = LOS_MemboxInit(g_swtmrHandlerPool, size, sizeof(SwtmrHandlerItem));
if (ret != LOS_OK) {
return LOS_ERRNO_SWTMR_HANDLER_POOL_NO_MEM;
}
for (UINT16 index = 0; index < LOSCFG_KERNEL_CORE_NUM; index++) {
SwtmrRunQue *srq = &g_swtmrRunQue[index];
/* The linked list of all cores must be initialized at core 0 startup for load balancing */
OsSortLinkInit(&srq->swtmrSortLink);
LOS_ListInit(&srq->swtmrHandlerQueue);
srq->swtmrTask = NULL;
}
SwtmrDebugDataInit();
return LOS_OK;
}
typedef VOID (*SWTMR_PROC_FUNC)(UINTPTR arg); //函數指標, 賦值給 SWTMR_CTRL_S->pfnHandler,回撥處理
typedef struct {//處理軟體定時器超時的回撥函數的結構體
SWTMR_PROC_FUNC handler; /**< Callback function that handles software timer timeout */ //處理軟體定時器超時的回撥函數
UINTPTR arg; /**< Parameter passed in when the callback function
that handles software timer timeout is called */ //呼叫處理軟體計時器超時的回撥函數時傳入的引數
LOS_DL_LIST node;
#ifdef LOSCFG_SWTMR_DEBUG
UINT32 swtmrID;
#endif
} SwtmrHandlerItem;
關於軟定時器可以檢視系列相關篇,請想想為何軟體定時器會使用靜態記憶體。
debug
一樣,文章內容會存在不少錯漏之處,請多包涵,但會反覆修正,持續更新,v**.xx
代表文章序號和修改的次數,精雕細琢,言簡意賅,力求打造精品內容。按功能模組:
百萬漢字註解核心目的是要看清楚其毛細血管,細胞結構,等於在拿放大鏡看核心。核心並不神祕,帶著問題去原始碼中找答案是很容易上癮的,你會發現很多文章對一些問題的解讀是錯誤的,或者說不深刻難以自圓其說,你會慢慢形成自己新的解讀,而新的解讀又會碰到新的問題,如此層層遞進,滾滾向前,拿著放大鏡根本不願意放手。
< gitee | github | coding | gitcode > 四大碼倉推播 | 同步官方原始碼,鴻蒙研究站 | weharmonyos 中回覆 百萬 可方便閱讀。
據說喜歡點贊分享的,後來都成了大神。