μC/OS-III---I笔记10---内存管理
内存管理:
平时经常用到一些windows内存管理的软件,有一些内存管理的软件进行内存碎片的整理,在频繁分配和释放内存的地方会造成大量的内存碎片。内存碎片是如何形成的呢?书中是这样写的:在不断的分配和释放内存的过程中,一整块内存被分散的小块内存,称为内存碎片。比如假设有8块连续的内存空间被分配使用了,后来一些被释放,最后内存使用的情况可能如图,红色表示还在占用着,这是如果要使用3块内存就无法分配了,但是实际上内存块是有的,只是不连续。内存管理就是为了避免出现这种情况,因此一般都是一次性取出较多的内存块,将多个内存块(只能是4,8,16等固定字节数)串成单链表,组成内存分区。用的时候从内存分区块中取出一块,用完再放回去。这种方式的时间很短,效率很高,但是在同一个内存分区中每次请求的内存块只能是固定大小的字节。

内存池的创建:
void OSMemCreate (OS_MEM *p_mem,
CPU_CHAR *p_name,
void *p_addr,
OS_MEM_QTY n_blks,
OS_MEM_SIZE blk_size,
OS_ERR *p_err)
{
#if OS_CFG_ARG_CHK_EN > 0u
CPU_DATA align_msk;
#endif
OS_MEM_QTY i;
OS_MEM_QTY loops;
CPU_INT08U *p_blk;
void **p_link;
CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif #ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif #if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { /* Not allowed to call from an ISR */
*p_err = OS_ERR_MEM_CREATE_ISR;
return;
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
//内存块的首地址不能为空
if (p_addr == (void *)0) { /* Must pass a valid address for the memory part. */
*p_err = OS_ERR_MEM_INVALID_P_ADDR;
return;
}
//内存块的大小不能小于2
if (n_blks < (OS_MEM_QTY)2) { /* Must have at least 2 blocks per partition */
*p_err = OS_ERR_MEM_INVALID_BLKS;
return;
}
//控制块必须要打过一个指针的大小,否则放不下一个地址
if (blk_size < sizeof(void *)) { /* Must contain space for at least a pointer */
*p_err = OS_ERR_MEM_INVALID_SIZE;
return;
}
//检查内存分区首地址有无数据对齐,内存分区块的大小是4的倍数
align_msk = sizeof(void *) - 1u;
if (align_msk > 0u) {
if (((CPU_ADDR)p_addr & align_msk) != 0u){ /* Must be pointer size aligned */
*p_err = OS_ERR_MEM_INVALID_P_ADDR;
return;
}
if ((blk_size & align_msk) != 0u) { /* Block size must be a multiple address size */
*p_err = OS_ERR_MEM_INVALID_SIZE;
return;
}
}
#endif
//将内存块串起来
p_link = (void **)p_addr; /* Create linked list of free memory blocks */
p_blk = (CPU_INT08U *)p_addr;
loops = n_blks - 1u;
for (i = 0u; i < loops; i++) {
p_blk += blk_size;
*p_link = (void *)p_blk; /* Save pointer to NEXT block in CURRENT block */
p_link = (void **)(void *)p_blk; /* Position to NEXT block */
}
*p_link = (void *)0; /* Last memory block points to NULL */ OS_CRITICAL_ENTER();
p_mem->Type = OS_OBJ_TYPE_MEM; /* Set the type of object */
p_mem->NamePtr = p_name; /* Save name of memory partition */
p_mem->AddrPtr = p_addr; /* Store start address of memory partition */
p_mem->FreeListPtr = p_addr; /* Initialize pointer to pool of free blocks */
p_mem->NbrFree = n_blks; /* Store number of free blocks in MCB */
p_mem->NbrMax = n_blks;
p_mem->BlkSize = blk_size; /* Store block size of each memory blocks */ #if OS_CFG_DBG_EN > 0u
OS_MemDbgListAdd(p_mem);
#endif
//内存管理对象++
OSMemQty++; OS_CRITICAL_EXIT_NO_SCHED();
*p_err = OS_ERR_NONE;
}
OSMemCreate ()
这里涉及数据对齐知识,比如一个32为数据总线的CPU要读取一个Byte的数据类型,CPU只会从4的倍数的开始地址一次性读取32为数据放到缓存里。然后根据需要剔除不要的部分,拼接合并后输送到内部寄存器里,读取一个字节的数据一次读写肯定是可以正确读取到所要的数据的,但如果读取多个字节的数据时,数据有分别在两个不同的4字节倍数的内存地址里,这是就需要两次内存读写访问和处理加长了数据读写的时间不利于快速读取,所以数据对齐有助于CPU快速的读取数据。对于程序中检测数据对齐和字节大小是否是4的倍数。比如32为CPU用sizeof(*void)结果是4,4-1为3二进制表示为0011B ,如果地址都是四字节对齐的那就是地址的最低两位就是0,即xxxxx00,再和0011B与就一定是0,同样其他对齐方式(4,8,16)也可以这样检测数据是否对齐。
内存管理变量结构体:

获取内存:
void *OSMemGet (OS_MEM *p_mem,
OS_ERR *p_err)
{
void *p_blk;
CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((void *)0);
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
if (p_mem == (OS_MEM *)0) { /* Must point to a valid memory partition */
*p_err = OS_ERR_MEM_INVALID_P_MEM;
return ((void *)0);
}
#endif CPU_CRITICAL_ENTER();
//内存池用完了?
if (p_mem->NbrFree == (OS_MEM_QTY)0) { /* See if there are any free memory blocks */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */
return ((void *)0); /* Return NULL pointer to caller */
}
//取出一个内存块,然后将第一个可用内存块地址向后移动一个
//方便下次内存获取
p_blk = p_mem->FreeListPtr; /* Yes, point to next free memory block */
p_mem->FreeListPtr = *(void **)p_blk; /* Adjust pointer to new free list */
p_mem->NbrFree--; /* One less memory block in this partition */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE; /* No error */
return (p_blk); /* Return memory block to caller */
}
这个函数会返回内存块的地址。
内存释放:
void OSMemPut (OS_MEM *p_mem,
void *p_blk,
OS_ERR *p_err)
{
CPU_SR_ALLOC(); #ifdef OS_SAFETY_CRITICAL
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif #if OS_CFG_ARG_CHK_EN > 0u
if (p_mem == (OS_MEM *)0) { /* Must point to a valid memory partition */
*p_err = OS_ERR_MEM_INVALID_P_MEM;
return;
}
if (p_blk == (void *)0) { /* Must release a valid block */
*p_err = OS_ERR_MEM_INVALID_P_BLK;
return;
}
#endif CPU_CRITICAL_ENTER();
//内存池已满
if (p_mem->NbrFree >= p_mem->NbrMax) { /* Make sure all blocks not already returned */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_MEM_FULL;
return;
}
//将下一个未使用内存块地址放入,将要放入内存池的内存块的
//的控制块里
*(void **)p_blk = p_mem->FreeListPtr; /* Insert released block into free block list */
//修改第一个未使用的内存块地址为将要放入内存池的内存块地址
p_mem->FreeListPtr = p_blk;
p_mem->NbrFree++; /* One more memory block in this partition */
CPU_CRITICAL_EXIT();
*p_err = OS_ERR_NONE; /* Notify caller that memory block was released */
}
无论是内存的获取和释放只要了解了内存池的数据结构,其实就是一个单项链表的删除和插入操作。创建内存分区的时候实际上是把指定的一个内存区域,划分称大小固定的内存块然后串称单向链表,在内存分配时,就用类似消息池的操作一样从内存中划分出一定的内存块来使用。所以内存管理实际上就是对内存池的一个“舀”和“倒”的操作。
μC/OS-III---I笔记10---内存管理的更多相关文章
- linux kernel学习笔记-5内存管理_转
void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...
- XV6学习笔记(2) :内存管理
XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...
- 《代码的未来》读书笔记:内存管理与GC那点事儿
一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...
- 嵌入式linux学习笔记1—内存管理MMU之虚拟地址到物理地址的转化
一.内存管理基本知识 1.S3C2440最多会用到两级页表:以段的方式进行转换时只用到一级页表,以页的方式进行转换时用到两级页表.页的大小有三种:大页(64KB),小页(4KB),极小页(1KB).条 ...
- iOS 阶段学习第九天笔记(内存管理)
iOS学习(C语言)知识点整理 一.内存管理 1)malloc , 用于申请内存; 结构void *malloc(size_t),需要引用头文件<stdlib.h>:在堆里面申请内存,si ...
- OC学习10——内存管理
1.对于面向对象的语言,程序需要不断地创建对象.这些对象都是保存在堆内存中,而我们的指针变量中保存的是这些对象在堆内存中的地址,当该对象使用结束之后,指针变量指向其他对象或者指向nil时,这个对象将称 ...
- Linux内核设计笔记12——内存管理
内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...
- Linux System Programming 学习笔记(九) 内存管理
1. 进程地址空间 Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间 内存页是memory management unit (MMU) 可以管理的最小地址单元 机器的体系 ...
- iOS阶段学习第21天笔记(ARC内存管理-Copy-代理)
iOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引 ...
- Linux内核学习笔记——内核内存管理方式
一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...
随机推荐
- Vue案例之todoLIst实现
使用Vue实现todolist案例,如有不对敬请大佬多多指教 功能: 1.增加功能:在新增版块里面的输入框内输入数据,点击后面的"添加"按钮,将输入的数据添加到列表中,默认是未完成 ...
- Flask的“中间件”
特殊装饰器 from flask import Flask,render_template,request app = Flask(__name__) @app.before_request def ...
- js 浮点数陷阱
众所周知,JavaScript 浮点数运算时经常遇到会 0.000000001 和 0.999999999 这样奇怪的结果,如 0.1+0.2=0.30000000000000004.1-0.9=0. ...
- 前端中的script标签
script标签中的重要属性! . 浏览器解析行内脚本的方式决定了它在看到字符串时,会将其当成结束的 标签.想避免这个问题,只需要转义字符"\" ①即可: 要包含外部文件中的 Ja ...
- 不支持的字符集 (在类路径中添加 orai18n.jar): ZHS16GBK
不支持的字符集 (在类路径中添加 orai18n.jar): ZHS16GBK 报错图示: 报错内容: Exception in thread "main" java.sql.SQ ...
- SQL函数知识点
SQL函数知识点 SQL题目(一) 1.查询部门编号为10的员工信息 select*from emp where empno=10; 2.查询年薪大于3万的人员的姓名与部门编号 select enam ...
- flume到底会丢数据吗?其可靠性如何?——轻松搞懂Flume事务机制
先给出答案: 需要结合具体使用的source.channel和sink来分析,具体结果可看本文最后一节. Flume事务 一提到事务,我们首先就想到的是MySQL中的事务,事务就是将一批操作做成原 ...
- 如何在Redis中实现事务
如何在Redis中实现事务 - 微店技术团队 - SegmentFault 思否 https://segmentfault.com/a/1190000007429197
- WPF和MVVM的结合使用方法,不可错过
Model:存储数据模型(类) 也在此业务逻辑,主要负责类文件的存储. ViewModel:连接View和Model,借助Command来负责界面的跳转和调用Model中方法来操作Model的数据. ...
- http状态码、错误分析
客户端的每一次请求,服务器都必须给出回应.回应包括 HTTP 状态码和数据两部分. HTTP状态码五大类: 状态码 响应类别 出现原因 1XX 信息性状态码(Informational) 服务器正在 ...