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