嵌入式自己管理指定大小的內存(類UCOS II),並結合循環隊列存儲數據

例子:存儲日誌,最多存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 ;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章