ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

嵌入式无操作系统下管理内存和队列(类UCOS II思想)

2022-02-24 19:02:00  阅读:188  来源: 互联网

标签:UCOS 队列 嵌入式 II 内存 日志 uint32 LogMCB LogQueue


例子:存储日志,最多存128条,每条最大1MB。

内存方面 因为嵌入式不适合用动态内存,会产生碎片。这里我们用 u8 data[LOG_SIZE];开辟固定128MB的内存区,再对其分为128个1MB内存块进行管理。
管理方法为:使用一个内存控制块结构体MCB,再编写增删改函数操作MCB进行管理。

队列方面我们使用循环队列,比如队列最多10个元素,我们存第11个元素时就会覆盖第一个。
管理方法为:使用一个队列控制块结构体LoopQueue,再编写增删改函数操作LoopQueue进行管理。

内存块与队列的关联是靠指针:
比如(存了信息的内存块1)与(队列位置1)
①我们可以把内存块1的地址指针存入队列位置1;
②也可以再定义个新结构体,包含内存块1的地址指针、信息的序号、信息的时间。再将这个结构体的指针存入队列位置1。

一、管理128MB内存

#define LOG_SIZE 0x8000000  //  (0x8000000 / 1024 / 1024= 128)
#define Blk_Num 128

//内存控制块结构体 Memory control block
typedef struct _MCB {
    void*   BeginAddr;  //指向开辟的内存的首地址
    uint32  TotalSize;  //开辟内存的总大小
    uint32  BlkSize;    //每个内存块的大小
    uint32  BlkNums;    //开辟的总内存块数目
    uint32  FreeBlks;   //可用的内存块数目
    uint32  BlkOut;     //获取内存时释放的位置
    uint32  BlkIn;      //回收内存时存放的位置
    uint32  MemAddr[Blk_Num];//该数组存储所有内存块的首地址
} MCB;

//对指定内存的实际操作就靠这两个全局变量
u8 data[LOG_SIZE];
MCB LogMCB;

//初始化,应该用函数执行,这里简单贴出来:
memset(data,0,sizeof(u8)*LOG_SIZE);

LogMCB.BeginAddr = data;
LogMCB.TotalSize = 0x8000000*sizeof(u8); //128MB
LogMCB.BlkSize   = 0x100000*sizeof(u8);  //1MB
LogMCB.BlkNums   = LogMCB.TotalSize/LogMCB.BlkSize;
LogMCB.FreeBlks  = LogMCB.BlkNums;
LogMCB.BlkIn     = 0;
LogMCB.BlkOut    = 0;
For(i=0;i<LogMCB.BlkNums;i++)
{
    LogMCB.MemAddr[i] = LogMCB.BeginAddr+(i*LogMCB.BlkSize);
}

//获取一块内存时各变量对应的操作:
LogMCB.MemAddr[LogMemObj.BlkOut] = 0; 置零
LogMCB.BeginAddr;   不变
LogMCB.TotalSize;   不变
LogMCB.BlkSize;     不变
LogMCB.BlkNums;     不变
LogMCB.FreeBlks--;  减一
LogMCB.BlkOut++;    加一
LogMCB.BlkIn;       不变

//释放一块内存时各变量对应的操作:
LogMCB.MemAddr[LogMemObj.BlkIn]  = addr;  赋值
LogMCB.BeginAddr;   不变
LogMCB.TotalSize;   不变
LogMCB.BlkSize;     不变
LogMCB.BlkNums;     不变
LogMCB.FreeBlks++;  加一
LogMCB.BlkOut;      不变
LogMCB.BlkIn++;     加一


代码为功能解析,实际使用应将初始化内存、获取内存、释放内存包装为函数。

在这里插入图片描述
//要理解内存控制结构体 每个成员的意义及用法
typedef struct _MCB {
void* BeginAddr; //指向开辟的内存的首地址
uint32 TotalSize; //开辟内存的总大小
uint32 BlkSize; //每个内存块的大小
uint32 BlkNums; //开辟的总内存块数目
uint32 FreeBlks; //可用的内存块数目
uint32 BlkOut; //获取内存时释放的位置
uint32 BlkIn; //回收内存时存放的位置
uint32 MemAddr[Blk_Num];//该数组存储所有内存块的首地址
} MCB;

//对指定内存的实际操作就靠这两个全局变量
u8 data[LOG_SIZE];
MCB LogMCB;

二、用循环队列管理日志
假如我们对日志是一直进行收发操作的,那么实际队列可能只用50条,就能满足128条日志的收发了。所以队列数不一定要内存块数这么大。

#define MAX 128
typedef struct _LoopQueue 
{
    uint32    QueueMax;     //队列最大数目
    uint32    QueueUsed;    //使用了的数目
    uint32    QueueIn;      //进队位置
    uint32    QueueOut;     //出队位置
    void      *Member[MAX]; //存储每条日志的结构体指针
} LoopQueue;

LoopQueue LogQueue;

//initial
LogQueue.QueueMax  = MAX;
LogQueue.QueueUsed = 0;
LogQueue.QueueIn   = 0;
LogQueue.QueueOut  = 0;
for(int i=0;i<MAX;i++)
{
    LogQueue.Member[i] = NULL;
}

//存储一条日志到内存块后,将内存块地址存入队列。我们发送日志时 直接操作队列、间接操作内存块:
void *nowlog = LogMCB.MemAddr[LogMemObj.BlkOut] ;
LogMCB.MemAddr[LogMCB.FreeRingOut]  = 0;
LogMCB.FreeBlks--;  
LogMCB.BlkOut++;    

LogQueue.Member[LogQueue.QueueIn]  =  (void *)nowlog ;
LogQueue.QueueUsed ++;
LogQueue.QueueIn   ++;

//删除一条日志时:
LOG * nlog = (LOG *)LogQueue.Member[LogQueue.QueueOut]  ;
LogQueue.Member[LogQueue.QueueOut] = NULL;
LogQueue.QueueOut ++;
LogQueue.QueueUsed --;

LogMCB.MemAddr[LogMCB.BlkIn]  = (void *)nlog;  
LogMCB.FreeBlks++;  
LogMCB.BlkIn++;     

//队列实现循环:
LogQueue.QueueIn  = LogQueue.QueueIn % LogQueue.QueueMax ;  //比如存第129条,会存储到物理第一条的位置
LogQueue.QueueOut = LogQueue.QueueOut  % LogQueue.QueueMax ; 

在这里插入图片描述
比较重要的就是内存初始化,内存使用,内存回收,队列初始化,队列使用,队列回收。对日志实际赋值以外很多地方都是用的指针进行交互。

主要要理解存储、删除日志时:内存的管理、队列的管理方式:
//存储一条日志时:
//1、使用一个内存块
void* nowlog = LogMCB.MemAddr[LogMCB.BlkOut] ;
LogMCB.MemAddr[LogMCB.FreeRingOut] = 0;
LogMCB.FreeBlks--;
LogMCB.BlkOut++;

//2、在地址处写日志(不一定用这种方法)
memcpy(nowlog ,pdata, 0x100000*sizeof(u8));

//3、将日志指针存入队列
LogQueue.Member[LogQueue.QueueIn] = (void )nowlog ;
LogQueue.QueueUsed ++;
LogQueue.QueueIn ++;

//删除一条日志时:

//1、取出队列中的日志指针
void
nowlog = (LOG *)LogQueue.Member[LogQueue.QueueOut] ;
LogQueue.Member[LogQueue.QueueOut] = NULL;
LogQueue.QueueOut ++;
LogQueue.QueueUsed --;
//2、该日志指针也是内存块首地址指针,回收该内存块
LogMCB.MemAddr[LogMCB.BlkIn] = (void *)nlog;
LogMCB.FreeBlks++;
LogMCB.BlkIn++;

//队列实现循环:

LogQueue.QueueIn = LogQueue.QueueIn % LogQueue.QueueMax ;
//比如存第129条,会存储到物理第一条的位置
LogQueue.QueueOut = LogQueue.QueueOut % LogQueue.QueueMax ;

标签:UCOS,队列,嵌入式,II,内存,日志,uint32,LogMCB,LogQueue
来源: https://www.cnblogs.com/solo666/p/15933176.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有