🪛RTOS原理与实现08:存储块实现
2023-6-4
| 2023-7-7
0  |  阅读时长 0 分钟
type
status
password
date
slug
summary
category
URL
tags
icon
notion image

1. 存储块的原理与创建

1.1 问题概述

1.1.1 内存分配的需求与问题

RTOS中也存在对内存分配与释放的需求,C库中提供的malloc & free函数用于实现任意指定大小的内存分配与释放,但是存在如下缺点,
  1. 频繁地进行任意大小的内存分配可能会产生很多不连续的细小的内存碎片,导致无法再次分配
    1. notion image
  1. 代码实现比较复杂,分配和释放过程操作时间不确定
    1. 需要遍历所有可用的内存块,以找到合适大小的内存块;如果要解决内存碎片问题,还会使得实现更加复杂

1.1.2 固定大小内存分配方案

在RTOS环境中,由于如下2个原因,不一定需要实现任意大小内存块的分配,
  1. 从程序最底层到应用层,所有开发组件都是可见的(e.g. 没有Linux用户态与内核态的区分)
  1. 针对特定场合开发,有时对存储空间分配大小的种类只有固定几种需求
以下图为例,在如下使用场景中,只需要提供3种固定大小的存储块即可,
notion image
当然,存储块固定大小分配也是有缺点的,
  1. 不论何时,不同的块类型总是独立持有各自的空闲存储块,彼此无法共享
  1. 在存储块内部可能存在碎片(当然,此处的内存碎片与上文介绍的性质不同,只要释放相应存储块,内存碎片即可得到利用)
notion image
 

1.2 设计原理

notion image
  1. 将存储空间以链表方式组织在空闲存储列表中
  1. 如果任务申请存储块时已无存储块可用,则在事件控制块上实现等待
💡
说明:在RTOS中,用于实现动态分配的内存块一般都是以全局数组的形式实现,与Linux中有本质不同

1.3 设计实现

1.3.1 定义存储控制块类型

💡
说明1:名词解释
存储块:可供分配的一段内存区域
存储控制块:用于组织与描述存储块的结构
💡
说明2:memStart指向的存储区会被划分为固定大小的存储块,而每个存储块的大小即为blockSize

1.3.2 添加tMemBlockInit函数

说明1:这里可以思考一下,此处使用存储块的前sizeof(tNode)字节作为链表结点,是否会影响用户使用整个blockSize的存储块空间 ? 答案是不会的,结合后续的获取 & 释放接口便可理解(本质上是对相同内存的不同解释方式)
说明2:在使用中,设置一个20 * 100的全局数据用于实现20个100B存储块

2. 存储块的获取与释放

2.1 设计原理

2.1.1 获取存储块

notion image
  1. 有空闲存储块,直接取出空闲块
  1. 无空闲存储块,任务进入等待队列

2.1.2 释放存储块

notion image
  1. 无任务等待,存储块插入到空闲存储链表
  1. 有任务等待,唤醒等待队列头部的任务,并传递存储块地址

2.2 设计实现

2.2.1 添加tMemBlockWait函数

说明:用户可以使用完整的blockSize内存
在将存储块组织为链表时,会将存储块最前端的8B作为链表结点使用,但是在返回存储块地址时使用
也就是将整个存储块的起始地址返回给请求存储块的任务

2.2.2 添加tMemBlockNoWaitGet函数

2.2.3 添加tMemBlockNotify函数

说明:在此可以看出,仅在将存储块组织为链表时才会将存储块的前端作为链表结点使用,所以这种组织方式并不会减少用户可使用的空间

实际应用

3. 存储块的删除与状态查询

3.1 设计原理

3.1.1 删除存储控制块

notion image
说明:由于用于分配的存储块是由全局数组实现的,所以在删除存储控制块时,只要删除在事件控制块上等待的任务即可
而被唤醒的任务将得到tErrorDel的返回值,说明存储控制块已被删除,这也再次说明判断这类wait接口返回值的重要性,并不是被唤醒就是资源就位

3.2 设计实现

3.2.1 添加tMemBlockDestroy函数

3.2.2 添加tMemBlockGetInfo函数

为了获取存储控制块的状态,首先定义用于保存存储控制块信息的结构
  • RTOS
  • RTOS原理与实现07:邮箱实现RTOS原理与实现09:事件标志组实现
    Loading...
    目录