μ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位系统:页 ...
随机推荐
- ElasticSearch-命令行客户端操作
1.引言 实际开发中,主要有三种方式可以作为elasticsearch服务的客户端: 第一种,elasticsearch-head插件(可视化工具) 第二种,使用elasticsearch提供的Res ...
- 转 16 jmeter中的监听器以及测试结果分析
16 jmeter中的监听器以及测试结果分析 常用监听器 断言结果.查看结果树.聚合报告.Summary Report.用表格查看结果.图形结果.aggregate graph等 指标分析 -Sa ...
- (08)-Python3之--类和对象
1.定义 类:类是抽象的,一类事物的共性的体现. 有共性的属性和行为. 对象:具体化,实例化.有具体的属性值,有具体做的行为. 一个类 对应N多个对象. 类包含属性以及方法. class 类名: 属 ...
- 让绝对定位的div居中
最近看到一个问题就是让绝对定位的div居中,在尝试了top:50%:left:50%:后发现,居中是有问题的并不是想象中的样子 需要再加两句margin-top:-盒子高度的一般px margin- ...
- no-referrer-when-downgrade
原因: 从一个网站链接到另外一个网站会产生新的http请求,referrer是http请求中表示来源的字段.no-referrer-when-downgrade表示从https协议降为http协议时不 ...
- 架构风格 vs. 架构模式 vs. 设计模式(译)
4.架构风格 vs. 架构模式 vs. 设计模式(译) - 简书 https://www.jianshu.com/p/d8dce27f279f
- High Performance Networking in Google Chrome 进程间通讯(IPC) 多进程资源加载
小结: 1. 小文件存储于一个文件中: 在内部,磁盘缓存(disk cache)实现了它自己的一组数据结构, 它们被存储在一个单独的缓存目录里.其中有索引文件(在浏览器启动时加载到内存中),数据文件( ...
- XSS、CSRF、SSRF联系&区别,防御
目录 区别和联系 防御 联系和区别 相同点: XSS,CSRF,SSRF三种常见的Web服务端漏洞均是由于,服务器端对用户提供的可控数据过于信任或者过滤不严导致的. 相同点: XSS,CSRF,SSR ...
- chrome标签记录——关于各类性能优化
概述 详情 概述 平时经常浏览各大博客,总感觉要学习和需要学习的内容太多太多,而自己的个人能力还不足够写出一些好的文章出来,就只能通过学习他人的东西不断提升自己的实力,然后就会记录收藏各种优秀的博客资 ...
- Mysql 5.5升级5.8
前言,因为升级跳板机,需要将mariadb 升级到10.2,也就是对应MySQL的5.8,废话不多说下面开始进行mariadb 5.5 的升级 Welcome to the MariaDB monit ...