v86.01 鴻蒙內核源碼分析 (靜態分配篇) | 很簡單的一位小朋友 | 百篇部落格分析 OpenHarmony 源碼
本篇關鍵詞:池頭、池體、節頭、節塊
記憶體管理相關篇為:
- v31.02 鴻蒙內核源碼分析(記憶體規則) | 記憶體管理到底在管什麼
- v32.04 鴻蒙內核源碼分析(物理記憶體) | 真實的可不一定精彩
- v33.04 鴻蒙內核源碼分析(記憶體概念) | RAM & ROM & Flash
- v34.03 鴻蒙內核源碼分析(虛實映射) | 映射是偉大的發明
- v35.02 鴻蒙內核源碼分析(頁表管理) | 映射關係保存在哪
- v36.03 鴻蒙內核源碼分析(靜態分配) | 很簡單的一位小朋友
- v37.01 鴻蒙內核源碼分析(TLFS演算法) | 圖表解讀TLFS原理
- v38.01 鴻蒙內核源碼分析(記憶體池管理) | 如何高效切割合併記憶體塊
- v39.04 鴻蒙內核源碼分析(原子操作) | 誰在守護指令執行的完整性
- v40.01 鴻蒙內核源碼分析(圓整對齊) | 正在製作中 ...
靜態分配
相比動態分配,靜態記憶體池的分配就是個小弟弟,非常的簡單,兩個結構體 + 一張圖 就能說明白。
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代表文章序號和修改的次數,精雕細琢,言簡意賅,力求打造精品內容。 - 百文在 < 鴻蒙研究站 | 開源中國 | 部落格園 | 51cto | csdn | 知乎 | 掘金 > 站點發布,鴻蒙研究站 | weharmonyos 中回復 百文 可方便閱讀。

按功能模組:
百萬注源碼 | 處處扣細節
-
百萬漢字註解內核目的是要看清楚其毛細血管,細胞結構,等於在拿放大鏡看內核。內核並不神秘,帶著問題去源碼中找答案是很容易上癮的,你會發現很多文章對一些問題的解讀是錯誤的,或者說不深刻難以自圓其說,你會慢慢形成自己新的解讀,而新的解讀又會碰到新的問題,如此層層遞進,滾滾向前,拿著放大鏡根本不願意放手。
-
< gitee | github | coding | gitcode > 四大碼倉推送 | 同步官方源碼,鴻蒙研究站 | weharmonyos 中回復 百萬 可方便閱讀。
據說喜歡點贊分享的,後來都成了大神。😃



