Open Blow-away opened 3 years ago
👴要OSMemQuery
我要OS_MemPut
我要OS_MemNameSet
OS_MemInit
nameset
nameget
# define OS_MAX_MEM_PART 3 // 内存分区的最大分区数 os_cfg.h
# define OS_MEM_NAME_EN 1 // 可以为分区设置名字 os_cfg.h
# define OS_MEM_EN 1 //是否开启内存管理 1:开始 0:关闭
注:以下分析都在 OS_MAX_MEM_PART>0 &&OS_MEM_EN >0 前提下才有意义**
1) 定义内存控制结构体
在内存管理开启且分区数目大于1的情况下,为了使系统能够感知和有效地管理内存分区,uC/OS II定义了一个叫做内存控制块的数据结构。系统就用这个内存控制块来记录和跟踪每一个内存分区的状态,数据结构类型如下:
// uc_os_ii.h
// 如果在内存管理开启且分区数目大于一的情况下才定义该结构体
// 根据是否可以为分区设置名字,判断是否在结构体中加入相关存储名字的变量
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
typedef struct os_mem // 内存控制结构体
{
void *OSMemAddr; // 内存分区开始指针
void *OSMemFreeList; // 指向 空闲块链表 的指针
INT32U OSMemBlkSize; // 每一块的大小,以byte为单位
INT32U OSMemNBlks; // 该分区的所有内存块的数目
INT32U OSMemNFree; // 该分区还剩下多少空闲内存块
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; // 内存分区名字
#endif
} OS_MEM;
2) 定义相关内存控制结构体的实例用于管理内存
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
OS_EXT OS_MEM *OSMemFreeList; // 指向内存分区可用列表的指针
OS_EXT OS_MEM OSMemTbl[OS_MAX_MEM_PART]; // 内存分区管理器的存储器数组,大小为分区数目
#endif
3) 辅助函数 : OS_MemClr
功能:清理一块连续的RAM
参数:pdest: 开始清理的地址 size:清理的byte数目
返回值:无
注意:1) 此函数是uC/OS-II的内部函数,用户应用程序不应调用它。
2) 请注意,我们最多只能清除64K字节的RAM。这不是问题,因为没有
函数的使用接近此限制。
3)一次清理一个字节,共清理size个字节,因为这将在任何处理器上工作,而不管目标的对齐情况。
// os_core.c
void OS_MemClr (INT8U *pdest, INT16U size)
{
while (size > 0u)
{
*pdest++ = (INT8U)0;
size--;
}
}
void OS_MemInit (void)
{
#if OS_MAX_MEM_PART == 1u
OS_MemClr ((INT8U *)&OSMemTbl[0], sizeof (OSMemTbl)); /* Clear the memory partition table */
OSMemFreeList = (OS_MEM *)&OSMemTbl[0]; /* Point to beginning of free list */
#if OS_MEM_NAME_EN > 0u
OSMemFreeList->OSMemName = (INT8U *)"?"; /* Unknown name */
#endif
#endif
#if OS_MAX_MEM_PART >= 2u
OS_MEM *pmem;
INT16U i;
OS_MemClr ((INT8U *)&OSMemTbl[0], sizeof (OSMemTbl)); /* Clear the memory partition table */
for (i = 0u; i < (OS_MAX_MEM_PART - 1u); i++) /* Init. list of free memory partitions */
{
pmem = &OSMemTbl[i]; /* Point to memory control block (MCB) */
pmem->OSMemFreeList = (void *)&OSMemTbl[i + 1u]; /* Chain list of free partitions */
#if OS_MEM_NAME_EN > 0u
pmem->OSMemName = (INT8U *) (void *)"?";
#endif
}
pmem = &OSMemTbl[i];
pmem->OSMemFreeList = (void *)0; /* Initialize last node */
#if OS_MEM_NAME_EN > 0u
pmem->OSMemName = (INT8U *) (void *)"?";
#endif
OSMemFreeList = &OSMemTbl[0]; /* Point to beginning of free list */
#endif
}
#endif /* OS_MEM_EN */
OS_MEM *OSMemCreate (void *addr,
INT32U nblks,
INT32U blksize,
INT8U *perr);
## 参数分析
- addr:内存分区的起始地址
- nblks:要将此分区分成几块内存
- blksize:每块内存的大小(单位为字节)
- perr:指向一个用来存放err信息的指针
- OS_ERR_NONE:成功创建
- OS_ERR_MEM_INVALID_ADDR:内存指定的地址无效,或指针未对齐边界(在块中)
- OS_ERR_MEM_INVALID_PART:没有空闲的内存块
- OS_ERR_MEM_INVALID_BLKS:指定了不合法的内存块数量(必须>=2)
- OS_ERR_MEM_INVALID_SIZE:指定了一个非法的内存块大小
- 必须大于一个指针的大小
- 必须能够容纳整数个指针
## 返回值
- NULL:创建失败
- !NULL:成功创建
## 具体分析
- OS_CRITICAL_METHOD:
- 表示进入临界区的实现方式:
- =1:直接开关中断
- =2:先用栈保存中断的开关状态,再关中断
- =3:用户可以得到当前处理器状态字(OS_CPU_SR)的值,并保存在C函数的局部变量中,此变量之后用于恢复PSW(程序状态字)
- OS_SAFETY_CRITICAL
- ucos做的安全认证,规范的一部分
- _IEC61508:IEC 61508认证
- OS_ARG_CHK_EN
- 检查参数条件
### uCOSii内存结构
- uCOSii 以内存分区来管理内存
- 每个内存分区有许多内存块,结构如图所示:
![memory](https://user-images.githubusercontent.com/47708214/101731505-adf7dc00-3af6-11eb-87a7-1e38dba7c463.png)
- 内存管理的核心是`OS_MEM`结构体,源代码如下
```c
typedef struct os_mem /* MEMORY CONTROL BLOCK */
{
void *OSMemAddr; /* 内存区的起始地址 */
void *OSMemFreeList; /* 指向首个空闲内存块 */
INT32U OSMemBlkSize; /* 每个内存块的大小(单位为byte) */
INT32U OSMemNBlks; /* 此内存区总共有多少内存块 */
INT32U OSMemNFree; /* 此内存区还有几个空闲内存块 */
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; /* Memory partition name */
#endif
} OS_MEM;
OS_ENTER_CRITICAL();
pmem = OSMemFreeList; /* Get next free memory partition */
if (OSMemFreeList != (OS_MEM *)0) /* See if pool of free partitions was empty */
{
OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList;
}
OS_EXIT_CRITICAL();
peme
是否指向了一个NULL,若指向NULL,错误返回
if (pmem == (OS_MEM *)0) /* See if we have a memory partition */
{
*perr = OS_ERR_MEM_INVALID_PART;
return ((OS_MEM *)0);
}
将每一内存块的首4个字节存入指向下一内存块的首地址的指针
plink = (void **)addr; /* Create linked list of free memory blocks */
pblk = (INT8U *)addr;
loops = nblks - 1u;
for (i = 0u; i < loops; i++)
{
pblk += blksize; /* pblk 指向下一个block的起始位置*/
*plink = (void *)pblk; /* 在现在内存块的存入下一内存块的起始位置 */
plink = (void **)pblk; /* 将plink指针位置移动至下一内存块的起始位置 */
}
*plink = (void *)0; /* 最后一个内存块一开始的下一起始位置存入NULL */
OS_MEM peme
初始化并返回peme
pmem->OSMemAddr = addr; /* Store start address of memory partition */
pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */
pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */
pmem->OSMemNBlks = nblks;
pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */
*perr = OS_ERR_NONE;
return (pmem);
函数签名如下:
void OSMemNameSet (OS_MEM pmem, INT8U pname, INT8U *perr)
无
OS_CRITICAL_METHOD:
OS_SAFETY_CRITICAL
OS_ARG_CHK_EN
OSIntNesting
首先检查是否是在中断处理例程中调用的此函数,如果是就直接报错并退出。
if (OSIntNesting > 0u) /* See if trying to call from an ISR */
{
*perr = OS_ERR_NAME_SET_ISR;
return;
}
之后进入临界区,设置名字。
OS_ENTER_CRITICAL();
pmem->OSMemName = pname;
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
一个指向指向内存分区控制块的指针。
在该函数中会使用的的信息:
OSMemNFree:该分区中空闲的内存块数量
OSMemNBlks:每个内存分区的大小
OSMemFreeList:一个指向空闲内存块链表的指针
一个指向正在被释放的内存块的指针。
返回一个错误代码。
pmem为空指针或指向未知的区域。
pblk为空指针或指向未知的区域。
表示内存完全被返回。
表示没有错误。
* (void **)pblk = pmem->OSMemFreeList; /* Insert released block into free block list */
pmem->OSMemFreeList = pblk;
pmem->OSMemNFree++; /* One more memory block in this partition */
具体过程:
(void *\)的作用:
图示:
if (pmem->OSMemNFree >= pmem->OSMemNBlks) /* Make sure all blocks not already returned */
{
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
具体过程:
此函数用于获得分配给内存分区的名称。。
函数签名如下:
INT8U OSMemNameGet (OS_MEM *pmem, INT8U *pname, INT8U perr)
在pmem不为空时,返回值为字符串的长度;pmem为空时,返回0
首先判断OS_CRITICAL_METHOD的值是否为3,如果等于3,将cpu_sr设置为0
OS_CRITICAL_METHOD:
*然后判断是否存在OS_ERR_MEM_INVALID_PMEM 、OS_ERR_PNAME_NULL 、OS_ERR_NAME_GET_ISR这三种情况,如果存在,则将perr赋予相应的宏,然后return 0;**
OS_SAFETY_CRITICAL
OSIntNesting
此处代码意为检查是否是在中断处理例程中调用此函数,如果是就直接报错并退出。
if (OSIntNesting > 0u) /* See if trying to call from an ISR */
{
*perr = OS_ERR_NAME_SET_ISR;
return;
}
最后进入临界区,将pmem里的OSMemName赋值给*pname,计算pname的长度,*perr赋值为OS_ERR_NONE ,即没有错误。
OS_ENTER_CRITICAL();
*pname = pmem->OSMemName;
len = OS_StrLen (*pname);
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
INT8U OSMemQuery (OS_MEM *pmem,
OS_MEM_DATA *p_mem_data)
{
pmem:一个指向指向内存分区控制块的指针
p_mem_data:用于写入获取的内存分区信息
OS_CRITICAL_METHOD:临界区进入模式
OS_ARG_CHK_EN:开启参数检查
OS_MEM_QUERY_EN:开启Query功能
OS_ENTER_CRITICAL();
p_mem_data->OSAddr = pmem->OSMemAddr;
p_mem_data->OSFreeList = pmem->OSMemFreeList;
p_mem_data->OSBlkSize = pmem->OSMemBlkSize;
p_mem_data->OSNBlks = pmem->OSMemNBlks;
p_mem_data->OSNFree = pmem->OSMemNFree;
OS_EXIT_CRITICAL();
进入临界区。
将所需信息从pmem拷贝到p_mem_data上。
这些信息包括:
属性名 | 含义 |
---|---|
OSMemAddr | 内存分区起始地址 |
OSMemFreeList | 空闲内存块链表 |
OSMemBlkSize | 单位内存块大小(byte) |
OSMemNBlks | 内存块总数 |
OSMemNFree | 空闲内存块总数 |
p_mem_data->OSNUsed = p_mem_data->OSNBlks - p_mem_data->OSNFree;
return (OS_ERR_NONE);
计算未使用内存块数(总内存块数减空闲内存块数)并返回。
从已经建立的内存分区中申请一个内存块,传入参数为pmem,指向特定分区内存控制块的指针,指向出错信息的指针perr,返回可用内存块地址
void OSMemGet (OS_MEM pmem, INT8U *perr)
通过定义移植文件OS_CPU.H中的常数OS_CRITICAL_METHOD来选择3中实现方法: • OS_CRITICAL_METHOD = 1 : 直接使用处理器的开关中断指令来实现宏 • OS_CRITICAL_METHOD = 2 : 利用堆栈保存和恢复CPU的状态 • OS_CRITICAL_METHOD = 3 : 利用编译器扩展功能获得程序状态字,保存在局部变量cpu_sr中 • OS_SAFETY_CRITICAL 安全认证,临界区的使用,对输入的空*peer抛出异常 • OS_ARG_CHE_EN 参数检查条件
内存控制块分配空闲内存块,(临界区控制操作) 进入临界区 有空闲内存块(>0) Pblk指针指向pmem上的空闲内存块List Pmem空闲内存块减一 退出临界区 返回对应信息和分配好的内存块地址
if (pmem->OSMemNFree > 0u)
{
pblk = pmem->OSMemFreeList;
pmem->OSMemFreeList = * (void **)pblk;
pmem->OSMemNFree--;
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
return (pblk);
}
Pmem的结构和分配好的pblk
void *OSMemGet (OS_MEM *pmem,
INT8U *perr)
{
void *pblk;
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL
if (perr == (INT8U *)0)
{
OS_SAFETY_CRITICAL_EXCEPTION();
return ((void *)0);
}
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0) /* Must point to a valid memory partition */
{
*perr = OS_ERR_MEM_INVALID_PMEM;
return ((void *)0);
}
#endif
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree > 0u) /* See if there are any free memory blocks */
{
pblk = pmem->OSMemFreeList; /* Yes, point to next free memory block */
pmem->OSMemFreeList = * (void **)pblk; /* Adjust pointer to new free list */
pmem->OSMemNFree--; /* One less memory block in this partition */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE; /* No error */
return (pblk); /* Return memory block to caller */
}
OS_EXIT_CRITICAL();
*perr = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */
return ((void *)0); /* Return NULL pointer to caller */
}
该函数为内存分区分配名称。
签名为void OSMemNameSet (OS_MEM *pmem,INT8U *pname,INT8U *perr)
无返回值
若从ISR调用,将perr置为OS_ERR_NAME_SET_ISR,然后退出。
继续执行,进入临界区,将pmem的OSMemName设为pname,退出临界区
其余部分与OSMemNameGet一致
我们编写一个简单的示例代码来测试我们的代码,这个示例仅创建一个task,这个task接收一定的参数(这个参数使用),然后点亮对应的LED灯,退出前释放内存:
#define DEMO_02_05 // demo2.5
#ifdef DEMO_02_05
#define TASK_PRIO 15
OS_MEM* data_mem;
OS_MEM* foo_mem;
static OS_STK task_stk[STK_SIZE_DEF];
static INT8U data[2][4];
static INT8U foo[3][16];
void task(void* p_arg);
#endif // DEMO_02_05
int User_App_Initial(void) {
// something else
#ifdef DEMO_02_05
data_mem = OSMemCreate(data, 2, 4, &err);
if (err) {
return err;
}
foo_mem = OSMemCreate(foo, 3, 16, &err);
if (err) {
return err;
}
void* pdata = OSMemGet(data_mem, &err);
if (err) {
return err;
}
INT8U* idata = (INT8U*)pdata;
idata[0] = idata[2] = 0;
idata[1] = idata[3] = 1;
err = OSTaskCreate(task, pdata, &task_stk[STK_SIZE_DEF - 1], TASK_PRIO);
if (err) {
return err;
}
#endif
return(0);
}
void task(void* p_arg) {
INT8U* temp = (INT8U*)p_arg;
if (temp[0] == 1) {
LED1_ON;
}
if (temp[1] == 1) {
LED2_ON;
}
if (temp[2] == 1) {
LED3_ON;
}
if (temp[3] == 1) {
LED4_ON;
}
INT8U err;
err = OSMemPut(data_mem, p_arg);
if (err) {
USER_USART1_print("can't return the block.\n");
}
}
此时LED2和LED4应该亮起。
我们尝试着从foo_mem
这个分区中拿走一块内存,并将其归还到data_mem
中:
INT8U err;
void* bar = OSMemGet(foo_mem, &err);
if (err) {
USER_USART1_print("can't get the block.\n");
}
err = OSMemPut(data_mem, bar);
if (err) {
USER_USART1_print("can't return the block.\n");
}
可以看到没有任何错误产生!UC/OS将内存块归还了回去。虽然目前没有发生任何错误,但这不是我们所期望的。
首先修改OS_MEM
结构体:
#if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u)
typedef struct os_mem /* MEMORY CONTROL BLOCK */
{
void *OSMemAddr; /* Pointer to beginning of memory partition */
void *OSMemFreeList; /* Pointer to list of free memory blocks */
#if OS_ARG_CHK_EN > 0u /*这个变量仅在进行检查的时候才使用,如果关闭了参数检查则可以不申明这个变量*/
void *OSMemTail; /*保存分区的尾部位置(闭区间,相当于--std::vector.end()),减少每次(头部指针+总大小)的运算*/
#endif
INT32U OSMemBlkSize; /* Size (in bytes) of each block of memory */
INT32U OSMemNBlks; /* Total number of blocks in this partition */
INT32U OSMemNFree; /* Number of memory blocks remaining in this partition */
#if OS_MEM_NAME_EN > 0u
INT8U *OSMemName; /* Memory partition name */
#endif
} OS_MEM;
然后在OSMemCreate
函数中为OSMemTai
变量做初始化:
#if OS_ARG_CHK_EN > 0u
pmem->OSMemTail = pblk;
#endif
最后修改OSMemPut
函数:
INT8U OSMemPut (OS_MEM *pmem,
void *pblk)
{
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0) /* Must point to a valid memory partition */
{
return (OS_ERR_MEM_INVALID_PMEM);
}
// if (pblk == (void *)0) /* Must release a valid block */
// {
// return (OS_ERR_MEM_INVALID_PBLK);
// }
// 不再需要这部分的检查,0始终是小于有效地址的
#endif
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree > pmem->OSMemNBlks) /* Make sure all blocks not already returned */
{
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
#if OS_ARG_CHK_EN > 0u
if (pblk < pmem->OSMemAddr || pblk >= pmem->OSMemTail) /*不是本分区的地址*/ {
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_INVALID_PBLK);
}
#endif
* (void **)pblk = pmem->OSMemFreeList; /* Insert released block into free block list */
pmem->OSMemFreeList = pblk;
pmem->OSMemNFree++; /* One more memory block in this partition */
OS_EXIT_CRITICAL();
return (OS_ERR_NONE); /* Notify caller that memory block was released */
}
现在再次运行之前的程序,可以发现内存块无法回收。
假定我们有多个这样的task需要操作,比如另一个task需要接收参数并启动一次蜂鸣器:
void bebop_task(void* p_arg); // 控制蜂鸣器
// 具体实现代码略
如果我们需要申请n个这样的内存块,那么我们需要重复调用OSMemGet
函数n次。我们扩展一个OSMemGetN
函数(对应的还有OSMemPutN
),允许我们更方便地操作内存。
首先在os_cfg.h
下添加新的裁剪宏:
#define OS_MEM_MULT_EN 1 /* 允许一次申请/归还多个内存块 */
在ucos_ii.h
下添加函数申明:
#if OS_MEM_MULT_EN > 0u
INT8U OSMemGetN(OS_MEM *pmem, void* blks[], INT8U count);
INT8U OSMemPutN(OS_MEM *pmem, void* blks[], INT8U count);
#endif
最后在os_mem.c
中给出函数实现:
#if OS_MEM_MULT_EN > 0u
/**
* 申请n个内存块
* @param pmem 内存分区指针
* @param blcks 分配的n个内存块指针地址数组
* @param count 需要多少个内存块
* @return INT8U 错误信息,与OSMemGet的err相同
*/
INT8U OSMemGetN(OS_MEM *pmem, void* blks[], INT8U count) {
INT8U i = 0;
void *pblk;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0)
{
return (OS_ERR_MEM_INVALID_PMEM);
}
if (blks == (INT8U**)0)
{
return (OS_ERR_MEM_INVALID_BLKS);
}
#endif
// 与单个申请相同的检查
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree >= count) // 剩余的内存块足够n个
{
for (i = 0; i < count; i++) {
pblk = pmem->OSMemFreeList;
blks[i] = pblk; // 申请到的内存块放入数组中
pmem->OSMemFreeList = * (void **)pblk; // 指向下一个可用节点
}
pmem->OSMemNFree -= count; // 可用数量减少
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
OS_EXIT_CRITICAL();
return OS_ERR_MEM_NO_FREE_BLKS; // 内存块数量不足
}
/**
* 归还n个内存块
* @param pmem 内存分区指针
* @param blcks 分配的n个内存块指针地址数组
* @param count 归还多少个内存块
* @return INT8U 错误信息,与OSMemGet的err相同
*/
INT8U OSMemPutN(OS_MEM *pmem, void* blks[], INT8U count) {
INT8U i;
void* pblk;
#if OS_CRITICAL_METHOD == 3u
OS_CPU_SR cpu_sr = 0u;
#endif
#if OS_ARG_CHK_EN > 0u
if (pmem == (OS_MEM *)0)
{
return (OS_ERR_MEM_INVALID_PMEM);
}
if (blks == (INT8U**)0)
{
return (OS_ERR_MEM_INVALID_BLKS);
}
#endif
// 同上的检查工作
OS_ENTER_CRITICAL();
if (pmem->OSMemNFree + count > pmem->OSMemNBlks) // 已经满了
{
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
#if OS_ARG_CHK_EN > 0u
for (i = 0; i < count; i++) {
pblk = blks[i]; // 逐个进行成分检查
if (pblk < pmem->OSMemAddr || pblk > pmem->OSMemTail) { // 成分不对,震怒!一个都不能归还
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_INVALID_PBLK);
}
}
#endif
for (i = 0; i < count; i++) { // 逐个归还
pblk = blks[i];
* (void **)pblk = pmem->OSMemFreeList;
pmem->OSMemFreeList = pblk;
}
pmem->OSMemNFree += count;
OS_EXIT_CRITICAL();
return (OS_ERR_NONE);
}
#endif
此时我们就可以一次分配两个内存块了:
// void* pdata = OSMemGet(data_mem, &err);
err = OSMemGetN(data_mem, args, 2);
if (err) {
return err;
}
为了实现统一回收,我们需要使用event:当每个task完成任务时,通知负责回收的task,由这个task统一完成(如果在同一个task中完成则不必):
INT8U i = 2, err;
while (i) {
OSSemPend(finish_semp, 0, &err);
if (err) {
USER_USART1_print("can't pend the sem.\n");
}
else {
i--;
}
}
err = OSMemPutN(data_mem, args, 2);
if (err) {
USER_USART1_print("can't return blocks.\n");
}
编译并烧录至板子中,效果与刚刚的代码是一样的。
增加函数的理由:
考虑如下的代码会发生什么:
void* bar1 = OSMemGet(foo_mem, &err);
if (err) {
return err;
}
void* bar2 = OSMemGet(foo_mem, &err);
if (err) {
return err;
}
err = OSMemPut(foo_mem, bar1);
if (err) {
return err;
}
err = OSMemPut(foo_mem, bar1);
if (err) {
return err;
}
OSMemPut
函数会二度把bar1
归还,并且不会抛出任何的错误!
我们为OSMemCreate
函数提供一个新的参数:
OS_MEM *OSMemCreate (void *addr,
INT32U nblks,
INT32U blksize,
INT8U *bitmap,
INT8U *perr);
这个bitmap
将在块被分配出去的时候,将对应的位置置1,回收时清零。如果回收的过程中对应的位置是0,那么这次回收就是重复回收,会被程序终止。
修改OS_MEM
结构体:
#if OS_ARG_CHK_EN > 0u
void *OSMemTail; /*分区尾部节点,节约时间,不必每次计算*/
INT8U *OSMemBitmap; /*bitmap,用来标记某个块是否已经被分配,避免每次到FreeList中查找*/
#endif
在创建的过程中为bitmap
赋值,并在OSMemGet(N)
和OSMemPut(N)
中分别添加:
// get
#if OS_ARG_CHK_EN > 0u
INT8U dis, rest;
dis = (INT8U)(pblk) - (INT8U)(pmem->OSMemAddr);
rest = dis - (dis >> 3);
dis >>= 3;
pmem->OSMemBitmap[dis] |= (1 << rest);
#endif
// put
#if OS_ARG_CHK_EN > 0u
INT8U dis, rest;
#endif
#if OS_ARG_CHK_EN > 0u
dis = (INT8U)(pblk) - (INT8U)(pmem->OSMemAddr);
rest = dis - (dis >> 3);
dis >>= 3;
if (!(pmem->OSMemBitmap[dis] & (1 << rest))) {
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
#else
if (pmem->OSMemNFree >= pmem->OSMemNBlks) /* Make sure all blocks not already returned */
{
OS_EXIT_CRITICAL();
return (OS_ERR_MEM_FULL);
}
#endif
现在我们的创建代码变为:
foo_mem = OSMemCreate(foo, 3, 16, &bitmap2, &err);
刚刚重复进行释放的代码也会出现相应的错误。
综述
os_mem.c
文件,观察有如下7个函数:OSMemCreate
OSMemGet
OSMemNameGet
OSMemNameSet
OSMemPut
OSMemQuery
OS_MemInit