上一篇文章写了通过系统调用开辟一块内存,那么问题来了,你是怎么开辟的内存?
这篇文章我们来探索一下鸿蒙内核开辟内存的过程并尝试改进liteos物理内存分配算法。
首先回顾一下上一次是怎么开辟的内存:添加一个系统调用
用到了LOS_MemAlloc函数
LITE_OS_SEC_TEXT VOID *LOS_MemAlloc(VOID *pool, UINT32 size)
{
VOID *ptr = NULL;
UINT32 intSave;
/*检查内存池指针和需要分配的大小是否合理*/
if ((pool == NULL) || (size == 0)) {
return (size > 0) ? OsVmBootMemAlloc(size) : NULL;
}
/*内存分配不被打断*/
MEM_LOCK(intSave);
do {
if (OS_MEM_NODE_GET_USED_FLAG(size) || OS_MEM_NODE_GET_ALIGNED_FLAG(size)) {
break;
}
ptr = OsMemAllocWithCheck(pool, size, intSave);
} while (0);
#ifdef LOSCFG_MEM_RECORDINFO
OsMemRecordMalloc(ptr, size);
#endif
MEM_UNLOCK(intSave);
return ptr;
}
你可以看到,在这个函数里面有一个OsMemAllocWithCheck,通过检查的内存分配
STATIC INLINE VOID *OsMemAllocWithCheck(VOID *pool, UINT32 size, UINT32 intSave)
{
/*动态内存节点*/
LosMemDynNode *allocNode = NULL;
/*开辟空间大小*/
UINT32 allocSize;
/*内存池信息*/
LosMemPoolInfo *poolInfo = (LosMemPoolInfo *)pool;
/*内存池第一个节点*/
const VOID *firstNode = (const VOID *)((UINT8 *)OS_MEM_HEAD_ADDR(pool) + OS_DLNK_HEAD_SIZE);
INT32 ret;
/*内存开辟合法性检查*/
if (OsMemAllocCheck(pool, intSave) == LOS_NOK) {
return NULL;
}
/*地址对齐*/
allocSize = OS_MEM_ALIGN(size + OS_MEM_NODE_HEAD_SIZE, OS_MEM_ALIGN_SIZE);
if (allocSize == 0) {
return NULL;
}
retry:
/*找到合适的空闲内存块*/
allocNode = OsMemFindSuitableFreeBlock(pool, allocSize);
/*没找到*/
if (allocNode == NULL) {
if (poolInfo->flag & MEM_POOL_EXPAND_ENABLE) {
ret = OsMemPoolExpand(pool, allocSize, intSave);
if (ret == 0) {
goto retry;
}
}
PRINT_ERR("---------------------------------------------------"
"--------------------------------------------------------\n");
MEM_UNLOCK(intSave);
OsMemInfoPrint(pool);
MEM_LOCK(intSave);
PRINT_ERR("[%s] No suitable free block, require free node size: 0x%x\n", __FUNCTION__, allocSize);
PRINT_ERR("----------------------------------------------------"
"-------------------------------------------------------\n");
return NULL;
}
/*找到了,但是太大了,需要分割*/
if ((allocSize + OS_MEM_NODE_HEAD_SIZE + OS_MEM_ALIGN_SIZE) <= allocNode->selfNode.sizeAndFlag) {
OsMemSplitNode(pool, allocNode, allocSize);
}
/*从可分配的内存链表里面删除*/
OsMemListDelete(&allocNode->selfNode.freeNodeInfo, firstNode);
/*给这块内存设置taskID和其他属性*/
OsMemSetMagicNumAndTaskID(allocNode);
OS_MEM_NODE_SET_USED_FLAG(allocNode->selfNode.sizeAndFlag);
if ((pool == (VOID *)OS_SYS_MEM_ADDR) || (pool == (VOID *)m_aucSysMem0)) {
OS_MEM_ADD_USED(OS_MEM_NODE_GET_SIZE(allocNode->selfNode.sizeAndFlag), OS_MEM_TASKID_GET(allocNode));
}
OsMemNodeDebugOperate(pool, allocNode, size);
return (allocNode + 1);
}
可以看到,对于内存块的分配部分,这里面的核心部分就是OsMemFindSuitableFreeBlock
STATIC INLINE LosMemDynNode *OsMemFindSuitableFreeBlock(VOID *pool, UINT32 allocSize)
{
LOS_DL_LIST *listNodeHead = NULL;
LosMemDynNode *tmpNode = NULL;
/*获取可以用的内存大小*/
UINT32 maxCount = (LOS_MemPoolSizeGet(pool) / allocSize) << 1;
UINT32 count;
#ifdef LOSCFG_MEM_HEAD_BACKUP
UINT32 ret = LOS_OK;
#endif
/*顺着往下找可用的空间*/
for (listNodeHead = OS_MEM_HEAD(pool, allocSize); listNodeHead != NULL;
listNodeHead = OsDLnkNextMultiHead(OS_MEM_HEAD_ADDR(pool), listNodeHead)) {
count = 0;
LOS_DL_LIST_FOR_EACH_ENTRY(tmpNode, listNodeHead, LosMemDynNode, selfNode.freeNodeInfo) {
if (count++ >= maxCount) {
PRINT_ERR("[%s:%d]node: execute too much time\n", __FUNCTION__, __LINE__);
break;
}
#ifdef LOSCFG_MEM_HEAD_BACKUP
if (!OsMemChecksumVerify(&tmpNode->selfNode)) {
PRINT_ERR("[%s]: the node information of current node is bad !!\n", __FUNCTION__);
OsMemDispCtlNode(&tmpNode->selfNode);
ret = OsMemBackupRestore(pool, tmpNode);
}
if (ret != LOS_OK) {
break;
}
#endif
if (((UINTPTR)tmpNode & (OS_MEM_ALIGN_SIZE - 1)) != 0) {
LOS_Panic("[%s:%d]Mem node data error:OS_MEM_HEAD_ADDR(pool)=%p, listNodeHead:%p,"
"allocSize=%u, tmpNode=%p\n",
__FUNCTION__, __LINE__, OS_MEM_HEAD_ADDR(pool), listNodeHead, allocSize, tmpNode);
break;
}
if (tmpNode->selfNode.sizeAndFlag >= allocSize) {
return tmpNode;
}
}
}
return NULL;
}
在寻找可用空间的时候,我们使用的是:OS_MEM_HEAD和OsDLnkNextMultiHead
其中,OS_MEM_HEAD的定义是这样的:
#define OS_MEM_HEAD(pool, size) OsDLnkMultiHead(OS_MEM_HEAD_ADDR(pool), size)
对于OsDLnkMultiHead:
LITE_OS_SEC_TEXT_MINOR LOS_DL_LIST *OsDLnkMultiHead(VOID *headAddr, UINT32 size)
{
LosMultipleDlinkHead *dlinkHead = (LosMultipleDlinkHead *)headAddr;
UINT32 index = OsLog2(size);
if (index > OS_MAX_MULTI_DLNK_LOG2) {
return NULL;
} else if (index <= OS_MIN_MULTI_DLNK_LOG2) {
index = OS_MIN_MULTI_DLNK_LOG2;
}
return dlinkHead->listHead + (index - OS_MIN_MULTI_DLNK_LOG2);
}
首先,在不改变内存调用代码的时候,执行内存开辟任务:
#include<stdio.h>
#include<syscall.h>
int main()
{
for(int i=1;i<6;i++)
{
syscall(SYS_wzsyscall,50);
syscall(SYS_wzsyscall,2000);
}
return 0;
}
得到的结果是:
下面开始修改,首先在memory.c文件里面变量定义的部分添加变量
//mem test to count node
int memNodeCount=0;
然后找到函数OsMemFindSuitableFreeBlock
对于修改之后的OsMemFindSuitableFreeBlock大家可以复制这段代码:
STATIC INLINE LosMemDynNode *OsMemFindSuitableFreeBlock(VOID *pool, UINT32 allocSize)
{
LOS_DL_LIST *listNodeHead = NULL;
LosMemDynNode *tmpNode = NULL;
UINT32 maxCount = (LOS_MemPoolSizeGet(pool) / allocSize) << 1;
UINT32 count;
#ifdef LOSCFG_MEM_HEAD_BACKUP
UINT32 ret = LOS_OK;
#endif
for (listNodeHead = OS_MEM_HEAD(pool, allocSize); listNodeHead != NULL;
listNodeHead = OsDLnkNextMultiHead(OS_MEM_HEAD_ADDR(pool), listNodeHead)) {
count = 0;
LOS_DL_LIST_FOR_EACH_ENTRY(tmpNode, listNodeHead, LosMemDynNode, selfNode.freeNodeInfo) {
memNodeCount++;//edit new
if (count++ >= maxCount) {
PRINT_ERR("[%s:%d]node: execute too much time\n", __FUNCTION__, __LINE__);
break;
}
#ifdef LOSCFG_MEM_HEAD_BACKUP
if (!OsMemChecksumVerify(&tmpNode->selfNode)) {
PRINT_ERR("[%s]: the node information of current node is bad !!\n", __FUNCTION__);
OsMemDispCtlNode(&tmpNode->selfNode);
ret = OsMemBackupRestore(pool, tmpNode);
}
if (ret != LOS_OK) {
break;
}
#endif
if (((UINTPTR)tmpNode & (OS_MEM_ALIGN_SIZE - 1)) != 0) {
LOS_Panic("[%s:%d]Mem node data error:OS_MEM_HEAD_ADDR(pool)=%p, listNodeHead:%p,"
"allocSize=%u, tmpNode=%p\n",
__FUNCTION__, __LINE__, OS_MEM_HEAD_ADDR(pool), listNodeHead, allocSize, tmpNode);
break;
}
if (tmpNode->selfNode.sizeAndFlag >= allocSize) {
if(allocSize == 2016 || allocSize == 72){ // edit new
PRINTK("count=%d,memNodeCount=%d, tmpNode=%p, allocSize=%d,tmpNode->selfNode.sizeAndFlag=%d\n",count,memNodeCount, tmpNode, allocSize, tmpNode->selfNode.sizeAndFlag);
}
return tmpNode;
}
}
}
return NULL;
}
测试代码如下:
#include<stdio.h>
#include<syscall.h>
int main()
{
for(int i=1;i<6;i++)
{
//printf("memorytest main......\n");
syscall(SYS_wzsyscall,54);
syscall(SYS_wzsyscall,2000);
}
return 0;
}
结果如下:
可以看到,memNodeCount不是每次都等于一,也就是说,不是一次就能找到合适的内存块。
好的,下面开始修改,best-fit修改成good-fit提升查找的速度:其实就是不要循环找那么多次了直接找个不大不小的差不多能用就行(不严谨的修改,只是为了简单测试运行逻辑)
使用到的文件和路径如下:
根据我们的一贯作风,还是分别修改.c文件和.h文件,首先修改.c文件
如果没有碰着边界,那么就直接index++,相当于不用在这一块找最接近的大小了,直接找一块更大的但又不是大太多的用。
代码如下:
//new good-fit func
LITE_OS_SEC_TEXT_MINOR LOS_DL_LIST *OsDLnkMultiHead_edit(VOID *headAddr, UINT32 size)
{
LosMultipleDlinkHead *dlinkHead = (LosMultipleDlinkHead *)headAddr;
UINT32 index = OsLog2(size);
if (index > OS_MAX_MULTI_DLNK_LOG2) {
return NULL;
} else if (index <= OS_MIN_MULTI_DLNK_LOG2) {
index = OS_MIN_MULTI_DLNK_LOG2;
}else if (index != OS_MAX_MULTI_DLNK_LOG2 && index !=OS_MIN_MULTI_DLNK_LOG2) {
index++;
}
return dlinkHead->listHead + (index - OS_MIN_MULTI_DLNK_LOG2);
}
下面修改相应的.h文件:
extern LOS_DL_LIST *OsDLnkMultiHead_edit(VOID *headAddr, UINT32 size);
使用工具改好了,那么该如何用呢?
下面就需要修改相应调用的部分了:
#define OS_MEM_HEAD_EDIT(pool, size) OsDLnkMultiHead_edit(OS_MEM_HEAD_ADDR(pool), size)
下面修改调用部分:
测试结果
可以看到每次的memNodeCount都是1,和预期一致