zoukankan      html  css  js  c++  java
  • RTX——第18章 内存管理

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php

    内存管理介绍
    在 ANSI C 中,可以用 malloc()和 free()2 个函数动态的分配内存和释放内存,但是,在嵌入式实时
    操作系统中,调用 malloc()和 free()却是危险的,因为多次调用这两个函数会把原来很大的一块连续内场
    区域逐渐地分割成许多非常小而且彼此又不相邻的内存块,也就是内存碎片。由于这些内存碎片的大量存
    在,使得程序到后来连一段非常小的连续内存也分配不到。另外,由于内存管理算法上的原因,malloc()
    和 free()函数的执行时间是不确定的。
    在 RTX 中,操作系统把连续的大块内存按分区来管理。每个分区中包含整数个大小相同的内存块。如
    图 18.1 所示:

    利用这种机制,就可以得到和释放固定大小的内存块。 这样内存的申请和释放函数的执行时间就是确定的了。
    在一个系统中可以有多个内存分区,这样,应用程序就可以从不同的内存分区中得到不同大小的内存
    块。但是特定的内存块在释放时,必须重新放回到它以前所属于的内存分区。显然,采用这样的内存管理
    算法,上面的内存碎片文件就得到了解决。
    其实 RTX 的内存管理也非常好理解,可以理解成一个二维数组,比如我们定义一个二维数组为:
    uint8_t mpool[10][32]。 对应到 RTX 的内存管理上就是定义了 10 个内存块,每块大小是 32 字节。
    如果还需要其它大小的内存块,还可以多定义几个其它大小的。

    内存管理 API 函数
    使用如下 7 个函数可以实现 RTX 的内存管理:
    _declare_box
    _declare_box8
    _init_box
    _init_box8
    _alloc_box
    _calloc_box
    _free_box

    函数_declare_box
    函数原型:
    #define _declare_box(
    pool, /* 内存池变量名 */
    size, /* 内存块大小,单位字节 */
    cnt ) /* 内存池中内存块的个数 */
    U32 pool[((size+3)/4)*(cnt) + 3]

    函数描述:
    函数_declare_box 用于定义一块内存池。
    第 1 个参数填写内存池的变量名。
    第 2 个参数填写内存块的大小,单位字节。
    第 3 个参数填写内存池中内存块的个数。
    使用这个函数要注意以下问题:
    1. 宏定义中通过操作(size+3)/4 保证了每个内存块大小是 4 字节的倍数,从而也就保证了每个内存块
    的首地址是 4 字节对齐的(4 字节对齐的含义是地址对 4 求余等于 0)。这里初学者也要注意数据类
    型的字节对齐问题。
    基本数据类型的字节对齐问题
    这个问题在 MDK5 安装目录里面的文档 DUI0375G_02_mdk_armcc_user_guide.pdf 里面有详细的说明,
    简单的说明, 数据类型有几个字节, 那么这个数据类型的变量就是几字节对齐,比如 32 位的 int 型
    有 4 个字节,那么此 int 型定义的变量就是 4 字节对齐,对于初学者要牢牢的记住这个知识点。

    其实这个在keil的帮助文档里面就有,善于利用ide的帮助文档,这是最直接快捷的熟悉编译器的方式。

    2. 内存池中额外定义的 12 个字节用于将内存块做指针链表,方便动态的申请和释放。

    函数_declare_box8
    函数原型:
    #define _declare_box8(
    pool, /* 内存池变量名 */
    size, /* 内存块大小,单位字节 */
    cnt ) /* 内存池中内存块的个数 */
    U64 pool[((size+7)/8)*(cnt) + 2]
    函数描述:
    函数_declare_box 用于定义一块内存池。
    第 1 个参数填写内存池的变量名。
    第 2 个参数填写内存块的大小,单位字节。
    第 3 个参数填写内存池中内存块的个数。
    使用这个函数要注意以下问题:
    1. 宏定义中通过操作(size+7)/8 保证了每个内存块大小是 8 字节的倍数,从而也就保证了每个内存块
    的首地址是 8 字节对齐的(8 字节对齐的含义是地址对 8 求余等于 0)。这里初学者也要注意数据类
    型的字节对齐问题。
    2. 内存池中额外定义的 16 个字节用于将内存块做指针链表,方便动态的申请和释放。

    函数_init_box
    函数原型:
    int _init_box (
    void* box_mem, /* 内存池首地址 */
    U32 box_size, /* 内存池大小,单位字节 */
    U32 blk_size ); /* 内存块大小, 单位字节 */
    函数描述:
    函数_init_box 用于内存池的初始化,初始化时用到的参数都是源自于_declare_box。
    第 1 个参数填写内存池的首地址。
    第 2 个参数填写内存池的大小,单位字节。
    第 3 个参数填写内存块大小,单位字节。
    使用这个函数要注意以下问题:

    1. 强烈建议跟函数_declare_box 一起使用。用户不要自己去初始化这个函数,用_declare_box 声明的
    才是最保险的。
    2. 如果用户没有使用函数_declare_box 进行定义,那么要保证内存池首地址是 4 字节对齐的。
    3. 如果用户没有使用函数_declare_box 进行定义,那么要保证内存池的大小 box_size 至少有 12 个字节,
    因为这个 12 个字节是用于将内存块做成指针链表,方便动态申请和释放。

    函数_init_box8
    函数原型:
    int _init_box (
    void* box_mem, /* 内存池首地址 */
    U32 box_size, /* 内存池大小,单位字节 */
    U32 blk_size ); /* 内存块大小, 单位字节 */
    函数描述:
    函数_init_box8 用于内存池的初始化,初始化时用到的参数都是源自于_declare_box8。
    第 1 个参数填写内存池的首地址。
    第 2 个参数填写内存池的大小,单位字节。
    第 3 个参数填写内存块大小,单位字节。
    使用这个函数要注意以下问题:
    1. 强烈建议跟函数_declare_box8 一起使用。用户不要自己去初始化这个函数,用_declare_box 声明的
    才是最保险的。
    2. 如果用户没有使用函数_declare_box8 进行定义,那么要保证内存池首地址是 8 字节对齐的。

    3. 如果用户没有使用函数_declare_box8 进行定义,那么要保证内存池的大小 box_size 至少有 16 个字
    节,因为这个 16 个字节是用于将内存块做成指针链表,方便动态申请和释放。

    函数_alloc_box
    函数原型:
    void *_alloc_box (
    void* box_mem ); /* 内存池的首地址 */
    函数描述:
    函数_alloc_box 用于从首地址是 box_mem 的内存池中申请一个内存块。
    第 1 个参数填写内存池的首地址。
    使用这个函数要注意以下问题:
    1. 调用此函数前,一定要使用函数_init_box 或者_init_box8 进行初始化。
    2. 函数_alloc_box 支持重入,而且是线程安全的,也即是说用户可以没有限制的在主函数和中断中调用此函数。
     函数_free_box
    函数原型:
    int _free_box (
    void* box_mem, /* 内存池首地址 */
    void* box ); /* 要释放的内存块首地址 */
    函数描述:
    函数_free_box 用于释放使用函数_alloc_box 申请的内存块。
    第 1 个参数填写内存池的首地址。
    第 2 个参数填写要释放的内存块首地址。
    使用这个函数要注意以下问题:
    1. 此函数的第二个参数必须要填写正确,也就是用户使用的时候最好配套_alloc_box 一起使用。
    2. 函数_free_box 支持重入,而且是线程安全的,也即是说用户可以没有限制的在主函数和中断中调用此函数。


    代码练兵场:

     

     

    串口打印:

    这里有两点需要说明:

    第一,为什么按键按下之后,先打印消息队列的输出,因为消息处理任务优先级高于按键处理任务(如果按键处理任务优先级高于消息处理,就会先打印按键消息);

    第二,在下面函数中采用官方demo的方式:

    我们可以使用void *pMsg,然后在wait中给&pMsg,最后打印的时候转换成自己想要的类型,也可以:

    直接给出想要的类型在wait函数中强转,安富莱就是使用的后者。

  • 相关阅读:
    read和write函数
    Android开发(20)--RadioGroup的使用
    利用Excel批量高速发送电子邮件
    NOTIFYICONDATA结构
    辞职信模板
    使用markdown语法撰写csdn博客
    算法笔记2-优先队列(堆)(上)
    湖南两初中女生水库溺亡的最新相关信息
    《cracking the coding intreview》——链表
    java算法集训代码填空题练习1
  • 原文地址:https://www.cnblogs.com/yangguang-it/p/7348156.html
Copyright © 2011-2022 走看看