μ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位系统:页 ...
随机推荐
- uni-app开发经验分享六:页面跳转及提示框
在我们开发的uni-app的过程中,页面跳转及提示框往往是我们做数据交互及结果反馈所要使用的功能,这里分享下我收集的一些方法及看法. 一:页面跳转 事件跳转 :指通过tap等事件来实现页面的跳转,跳转 ...
- Python攻城——查看,生成幫助文檔
1. python在控制台中查看文檔 1 python -m pydoc 模塊名 2. pydoc生成HTML文檔 1 python -m pydoc -w 模塊名 1 python -m pydoc ...
- 推荐大家去撸60元的阿里云ACA DevOps认证
要试题的右边扫码支付10元,私聊博客哈,说出你微信号,留下邮箱,发你邮箱Pdf文件,这么便宜拿证!!
- OPTIONS的预请求(Preflighted Request)
OPTIONS的预请求(Preflighted Request) Ajax 请求中出现OPTIONS(Request Method: OPTIONS)_qiao-CSDN博客 https://blog ...
- V2版的接口在V3版里面都能找到对应接口 数据结构
开发文档 - 微信支付商户平台 https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pages/api.shtml 版本选择 关闭 V2版接口和V3版接口实际 ...
- SumatraPDF设置护眼背景
高级选项中: 1 FixedPageUI [ 2 TextColor = #000000 3 BackgroundColor = #C7EDCC 4 SelectionColor = #f5fc0c ...
- 游戏中的AOI(Area of Interest)算法
游戏中的AOI(Area of Interest)算法 游戏的AOI算法应该算作游戏的基础核心了,许多逻辑都是因为AOI进出事件驱动的,许多网络同步数据也是因为AOI进出事件产生的.因此,良好的AOI ...
- mac或linux常见命令
1. 用某个软件打开某文件,如subline text: https://blog.csdn.net/Cinderella_hou/article/details/82392139 如果想使用subl ...
- Springboot 项目部署到服务器上
项目部署到服务器上,有两种方式,一种 jar 包,一种 war 包 jar包 部署时,后续的域名配置,SSL证书等在nginx中配置 war包 部署时,后续的域名配置可以在tomcat中配置就好,修改 ...
- 安装Archlinux+UEFI启动
为了安装Arch自己也走了很多弯路,找了很多教程,最后探索出了这样一个安装方法,不一定适用于每个人. ArchWiki官方安装手册 本方法全程插上网线. 准备 获取镜像 镜像可以从官网获取,访问官方下 ...