关于RTOS的全部真相。 第十四条 内存部分:简介和基本服务



前面的一篇文章(#6)中前面提到了内存的各个部分,其中与C malloc()语言的标准功能进行了比较。 分区是从分区池(内存池)获得的内存区域。 共享内存提供了一种灵活而可靠的方法,可以确定地分配和释放内存。

该系列中的先前文章:
第十三条 任务数据结构和不受支持的API调用
第十二条 任务处理服务
第11条 任务:API的配置和介绍
第10条 计划程序:高级功能和上下文保留
第9条。 调度程序:实施
第8条 Nucleus SE:内部设计和部署
第7条 Nucleus SE:简介
第6条。 其他RTOS服务
第5条 任务交互和同步
第4条 任务,上下文切换和中断
第3条 任务与计划
第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。

使用部分


在Nucleus SE中,分区池是在创建时配置的。 一个应用程序最多可以有16个分区池。 如果未配置它们,则与这些池相关的数据结构和服务调用将不包含在应用程序中。

分区池是划分为一定数量的固定大小的块的存储区域。 开发人员可以完全控制每个池中分区的大小和数量。 任务可以请求分配的内存部分,并接收指向存储区域的指针,并且不应在分配的部分之外写入数据。 将指针传递给API函数时,任何任务都可以释放该节。 在没有可用分区的情况下分配分区的请求可能导致错误或请求中止,具体取决于所选的API调用参数和Nucleus SE配置。

设置内存分区


分区池数


与大多数Nucleus SE对象一样,分区池配置主要使用nuse_config.h中#define指令完成 。 主要参数是NUSE_PARTITION_POOL_NUMBER ,它确定在应用程序中定义了多少个分区池。 默认值为0(即不使用分区池),开发人员可以设置0到16之间的任何值。其他值将导致编译错误,该错误在nuse_config_check.h验证过程中检测到(包含在nuse_config.c中 ,并且,则使用此模块进行编译),从而导致#error指令的编译。

选择非零值是激活分区池的优先方法。 这导致了数据结构的定义和适当大小的分配。 ROM中的数据结构必须使用描述每个分区池的适当值进行初始化。 下一篇文章将提供有关数据结构的更多详细信息。 此选择还会激活API设置。

激活API调用


Nucleus SE中的每个API函数(实用程序调用) 都由nuse_config.h中#define指令激活。 对于分区池,这些包括:

NUSE_PARTITION_ALLOCATE
NUSE_PARTITION_DEALLOCATE
NUSE_PARTITION_POOL_INFORMATION
NUSE_PARTITION_POOL_COUNT

默认情况下,它们都设置为FALSE ,从而禁用每个服务调用并防止包含实现代码。 要在应用程序中配置分区池,您需要选择必要的API调用并将相应的指令设置为TRUE

以下是默认nuse_config.h文件的摘录:



如果激活了分区池API功能,但未配置池,则会发生编译错误(始终启用的NUSE_Partition_Pool_Count()除外)。 如果您的代码使用尚未激活的API调用,则会发生布局错误,因为实现代码未包含在应用程序中。

分区池实用程序调用


Nucleus RTOS支持与分区池有关的七个实用程序调用,它们提供以下功能:

功能说明核RTOS核SE
选区NU_Allocate_Partition()NUSE_Partition_Allocate()
部分发布NU_Deallocate_Partition()NUSE_Partition_Deallocate()
提供信息
关于特定分区池
NU_Partition_Pool_Information()NUSE_Partition_Pool_Information()
返回(当前)已配置数量的值
应用程序池
NU_Established_Partition_Pools()NUSE_Partition_Pool_Count()
向应用程序添加(创建)新的分区池NU_Create_Partition_Pool()未实施。
从应用程序更改(删除)分区池NU_Delete_Partition_Pool()未实施。
返回指向应用程序中当前存在的所有分区池的指针NU_Partition_Pool_Pointers()未实施。

将详细讨论每个调用的实现。

值得注意的是,Nucleus RTOS和Nucleus SE都没有重启功能。 这是有目的的。 通常,一个任务分配一个节并将一个指针传递给另一个任务(稍后可能会释放它)。 如果重新加载分区池,则所有分区都将标记为未使用,但是,没有机制可以监视和通知所有可以使用分区的任务。

分区和发布服务


分区池的基本操作是在池中分配分区(即,将分区标记为已使用并返回其地址)并释放该分区(即,该分区被标记为未使用)。 Nucleus RTOS和Nucleus SE为这些操作提供了两个基本的API调用,如下所述。

选区


Nucleus RTOS API调用分配分区非常灵活,如果无法立即完成操作,例如当您尝试从已经分配了所有分区的池中分配分区时,开发人员可以无限期地暂停任务或在没有超时的情况下暂停任务。 Nucleus SE提供相同的服务,只是其中的暂停任务是可选的,并且未实现超时。

Nucleus RTOS API分区调用


调用原型:

状态NU_Allocate_Partition(NU_PARTITION_POOL *池,VOID ** return_pointer,未签名挂起);

返回值:

NU_SUCCESS-调用成功完成;
NU_NO_PARTITION-没有可用的部分;
NU_INVALID_POOL-无效的分区池指针;
NU_INVALID_POINTER-将空指针传递给返回的数据( NULL );
NU_INVALID_SUSPEND-尝试从与任务无关的线程挂起任务;
NU_TIMEOUT-即使暂停了指定的等待时间,也没有可用的分区;
NU_POOL_DELETED-挂起任务时删除了分区池。

Nucleus SE API调用以突出显示分区


该API调用支持Nucleus RTOS API的核心功能。

调用原型:

状态NUSE_Partition_Allocate(NUSE_PARTITION_POOL池,ADDR * return_pointer,U8挂起);

参数:

pool-使用的分区池的索引(ID);
return_pointer-指向ADDR类型变量的指针,该变量采用所选节的地址;
暂停 -用于暂停任务的参数;它可以采用值NUSE_NO_SUSPENDNUSE_SUSPEND

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_NO_PARTITION-没有可用的部分;
NUSE_INVALID_POOL-无效的分区池索引;
NUSE_INVALID_POINTER-将空指针传递给返回的数据( NULL );
NUSE_INVALID_SUSPEND-尝试从与任务不相关的线程或禁用锁定API时挂起任务。

Nucleus SE中的分区分配实现


在检查参数之后,使用条件编译选择API函数代码NUSE_Partition_Allocate ,具体取决于是否激活了对块的API调用(挂起任务)。 下面我们将分别考虑这两个选项。

如果禁用了阻塞调用,则API调用非常简单:



首先,检查可用分区的可用性。 如果没有这样的分区,则返回错误( NUSE_NO_PARTITION )。 然后是部分的枚举,在此期间检查第一个字节是否为零值(指示未使用该部分)。 找到这样的分区后,将为其分配“ used”标志,其中包括分区池的索引(请参见下面的“释放分区”),并返回指向下一个字节(真实数据区域的开始)的指针。 有关分区池的数据结构的说明将在下一篇文章的“数据结构”部分中介绍。

如果激活了锁定,则此API调用的代码会变得更加复杂:



该代码包含在do ... while循环中 ,只要pause参数为NUSE_SUSPEND ,该循环就会继续运行。

如果没有可用的分区,并且pause参数为NUSE_NO_SUSPEND ,则API调用将停止并返回NUSE_NO_PARTITION 。 如果暂停参数设置为NUSE_SUSPEND ,任务将暂停。 当返回时(例如,当任务恢复时), NUSE_SUCCESS的返回值指示该任务已恢复,因为释放了内存部分,并且代码返回到循环的开头。 由于没有用于重新加载分区池的API函数,因此,由于其他原因无法恢复任务,但是为了稳定阻止其他类型的对象,保留了NUSE_Task_Blocking_Return []验证过程。

部分发布


Nucleus RTOS和Nucleus SE中该部分的发布使其再次可用。 在发布之前,它不会检查此部分是否被任何任务使用,应用程序程序员对此负责。 仅需要指向数据区域的指针即可释放节。

Nucleus RTOS API调用以进行免费分区


调用原型:

状态NU_Deallocate_Partition(VOID *分区);

参数:

partition-指向要释放的分区的数据区域(由NU_Allocate_Partition()函数返回的指针;

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_POINTER-空节指针,或不指示使用的有效节。

Nucleus SE API调用到自由分区


该API调用支持Nucleus RTOS API的核心功能。

调用原型:

STATUS NUSE_Partition_Deallocate(ADDR分区);

参数:

partition-指向要释放的分区的数据区域(由NUSE_Partition_Allocate()函数返回的指针

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_POINTER-节指针为null( NULL ),或者不表示使用的有效节

实作


NUSE_Partition_Deallocate()函数没有包含使用阻塞和非阻塞API函数来实现, 而是仅包含一个有条件编译的部分,该部分负责解锁任务。 这段代码实现了节的释放:



首先,从状态字节中检索节索引。 然后,分区的状态更改为“未使用”,已使用分区的计数器减少,并且该功能报告操作成功完成。

如果激活了锁定,则以下代码用于恢复等待可用分区池的任务:



如果在此池中分配分区时任务被阻止,则将恢复第一个表。

在下一篇文章中,我们将讨论与内存分区以及相关数据结构有关的其他API调用。

关于作者: Colin Walls在电子行业工作了30多年,大部分时间用于固件。 他现在是Mentor Embedded(Mentor Graphics的一个部门)的固件工程师。 Colin Walls经常在会议和研讨会上发表演讲,他撰写了许多技术文章并撰写了两本有关固件的书。 居住在英国。

Source: https://habr.com/ru/post/zh-CN426425/


All Articles