内存管理:

平时经常用到一些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---内存管理的更多相关文章

  1. linux kernel学习笔记-5内存管理_转

    void * kmalloc(size_t size, gfp_t gfp_mask); kmalloc()第一个参数是要分配的块的大小,第一个参数为分配标志,用于控制kmalloc()的行为. km ...

  2. XV6学习笔记(2) :内存管理

    XV6学习笔记(2) :内存管理 在学习笔记1中,完成了对于pc启动和加载的过程.目前已经可以开始在c语言代码中运行了,而当前已经开启了分页模式,不过是两个4mb的大的内存页,而没有开启小的内存页.接 ...

  3. 《代码的未来》读书笔记:内存管理与GC那点事儿

    一.内存是有限的 近年来,我们的电脑内存都有好几个GB,也许你的电脑是4G,他的电脑是8G,公司服务器内存是32G或者64G.但是,无论内存容量有多大,总归不是无限的.实际上,随着内存容量的增加,软件 ...

  4. 嵌入式linux学习笔记1—内存管理MMU之虚拟地址到物理地址的转化

    一.内存管理基本知识 1.S3C2440最多会用到两级页表:以段的方式进行转换时只用到一级页表,以页的方式进行转换时用到两级页表.页的大小有三种:大页(64KB),小页(4KB),极小页(1KB).条 ...

  5. iOS 阶段学习第九天笔记(内存管理)

    iOS学习(C语言)知识点整理 一.内存管理 1)malloc , 用于申请内存; 结构void *malloc(size_t),需要引用头文件<stdlib.h>:在堆里面申请内存,si ...

  6. OC学习10——内存管理

    1.对于面向对象的语言,程序需要不断地创建对象.这些对象都是保存在堆内存中,而我们的指针变量中保存的是这些对象在堆内存中的地址,当该对象使用结束之后,指针变量指向其他对象或者指向nil时,这个对象将称 ...

  7. Linux内核设计笔记12——内存管理

    内存管理学习笔记 页 页是内核管理内存的基本单位,内存管理单元(MMU,管理内存并把虚拟地址转化为物理地址的硬件)通常以页为单位进行处理,从虚拟内存的角度看,页就是最小单位. struct page{ ...

  8. Linux System Programming 学习笔记(九) 内存管理

    1. 进程地址空间 Linux中,进程并不是直接操作物理内存地址,而是每个进程关联一个虚拟地址空间 内存页是memory management unit (MMU) 可以管理的最小地址单元 机器的体系 ...

  9. iOS阶段学习第21天笔记(ARC内存管理-Copy-代理)

    iOS学习(OC语言)知识点整理 一.OC 中的ARC内存管理 1)ARC中释放对象的内存原则:看这个对象有没有强引用指向它 2)strong:强引用,默认情况下的引用都是强引用 3) weak:弱引 ...

  10. Linux内核学习笔记——内核内存管理方式

    一 页 内核把物理页作为内存管理的基本单位:内存管理单元(MMU)把虚拟地址转换为物理 地址,通常以页为单位进行处理.MMU以页大小为单位来管理系统中的也表. 32位系统:页大小4KB 64位系统:页 ...

随机推荐

  1. 从零开始学spring源码之xml解析(二):默认标签和自定义标签解析

    默认标签: 上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的.决定还是拆开来写.今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签: private void parseDe ...

  2. jdk安装简洁版

    一.jdk是what? jdk其实是个软件语言开发包,包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具. 没有JDK的话,无法编译Java程序(指java源码.java文件),如果想 ...

  3. 使用Linux服务器来通过网络安装和激活Windows 7 —— 一些基本原理

    使用Linux服务器来通过网络安装和激活Windows 7 -- 一些基本原理 https://www.pufengdu.org/blog/?p=372

  4. pycharm设置头文件模板(for mac)

    我们要达到的效果是每次新建一个.py文件都会有头文件,关于头文件的作用之前已做解释

  5. Linux 输入输出重定向, &>file, 2>&1, 1>&2

    Linux 输入输出重定向, &>file, 2>&1, 1>&2 一.1和2在Linux中代表什么 1.1 输出重定向 1.2 输入重定向 1.3 绑定重定 ...

  6. 枚举--让盗版美国总统wcc给你整明白哈哈

    1.为什么要有枚举 Java中的枚举其实是一种语法糖,在 JDK 1.5之后出现,用来表示固定且有限个的对象.比如一个季节类有春.夏.秋.冬四个对象:一个星期有星期一到星期日七个对象.这些明显都是固定 ...

  7. XV6学习(11)Lab thread: Multithreading

    代码放在github上. 这一次实验感觉挺简单的,特别是后面两个小实验.主要就是对多线程和锁进行一个学习. Uthread: switching between threads 这一个实验是要实现一个 ...

  8. C++模板的介绍

    ​ 作者:良知犹存 转载授权以及围观:欢迎添加微信:Allen-Iverson-me-LYN     1. 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码.C++模板的作用,类 ...

  9. java中== equal hashcode 比较的区别

    == 基本数据类型是进行数值的比较 引用数据类型比较的是两对象的地址值 实际上都是进行直接值的比较 equal(Object) Object型参数,任何类型的实参都可以传入,只有实参是一个字符串且内容 ...

  10. switch表达式为字符串

    package EXERCISE; import java.util.*; public class HashCode { //switch判断字符串.switch表达式byte,short,int, ...