关于RTOS的全部真相。 第24条。 队列:辅助服务和数据结构



在本文中,我们将继续考虑队列。

次要队列服务


Nucleus RTOS有四个API调用,它们提供与队列相关的辅助功能:重置队列,接收队列信息,获取应用程序中队列的数量以及获取指向应用程序中所有队列的指针。 前三个功能在Nucleus SE中实现。

该系列中的先前文章:

第23条 队列:简介和基本服务
第22条 邮箱:辅助服务和数据结构
第21条。 邮箱:简介和基本服务
第20条 信号量:辅助服务和数据结构
第十九条 信号灯:简介和基本服务
第十八条 事件标志组:助手服务和数据结构
第十七条 事件标志组:简介和基本服务
第十六条 讯号
第十五条 内存分区:服务和数据结构
第十四条 内存部分:简介和基本服务
第十三条 任务数据结构和不受支持的API调用
第十二条 任务处理服务
第11条 任务:API的配置和介绍
第10条 计划程序:高级功能和上下文保留
第9条。 调度程序:实施
第8条 Nucleus SE:内部设计和部署
第7条 Nucleus SE:简介
第6条。 其他RTOS服务
第5条 任务交互和同步
第4条 任务,上下文切换和中断
第3条 任务与计划
第2条。 RTOS:结构和实时模式
第1条 RTOS:简介。

队列重置


此API调用将队列重置为其原始的未使用状态。 队列中存储的所有消息都将丢失。 队列中暂停的所有任务将以返回码NUSE_QUEUE_WAS_RESET恢复。

Nucleus RTOS中的队列重置调用
服务电话原型:
状态NU_Reset_Queue(NU_QUEUE *队列);

参数:
队列 -指向用户提供的队列控制块的指针。

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_QUEUE-无效的队列指针。

Nucleus SE中的队列重置呼叫
该实用程序调用支持Nucleus RTOS API的核心功能。

服务电话原型:
状态NUSE_Queue_Reset(NUSE_QUEUE队列);

参数:
队列 -转储队列的索引(ID)。

返回值:
NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_QUEUE-无效的队列索引。

在Nucleus SE中实施队列重置
NUSE_Queue_Reset函数的代码(检查参数后)非常简单。 队列头部和尾部的索引以及队列中的消息计数器被分配为零值。

如果激活了任务锁定,则其他代码负责恢复挂起的任务:

while (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this queue */ for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { NUSE_Task_Blocking_Return[index] = NUSE_QUEUE_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Queue_Blocking_Count[queue]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

队列中的每个暂停任务都被分配为“就绪”状态,返回码为NUSE_QUEUE_WAS_RESET 。 完成此过程后,如果使用优先级调度程序,则会调用NUSE_Reschedule()函数,因为可以准备执行一个或多个具有高优先级的任务。

获取队列信息


该服务调用提供队列信息。 Nucleus SE中此调用的实现与Nucleus RTOS的不同之处在于它返回的信息较少,因为不支持对象命名,可变消息长度和任务暂停顺序,并且可以禁用任务锁定。

在Nucleus RTOS中调用队列信息
服务电话原型:

状态NU_Queue_Information(NU_QUEUE *队列,CHAR *名称,VOID **起始地址,UNSIGNED * queue_size,UNSIGNED *可用,UNSIGNED *消息,OPTION * message_type,UNSIGNED * message_size,OPTION *暂停类型,UNSIGNED * first_task_task_task

参数:

queue-指向用户提供的队列控制块的指针;
name-指向队列中消息名称的8个字符区域的指针;
start_address-指向将写入队列数据区开始地址的指针的指针;
queue_size-指向变量的指针,该变量用于存储队列中UNSIGNED元素的总数;
可用 -指向变量的指针,该变量用于存储队列中可用的UNSIGNED元素的数量;
messages-指向存储队列中当前消息数的变量的指针;
message_type-指向变量的指针,该变量用于存储队列支持的消息类型。 有效值为NU_FIXED_SIZENU_VARIABLE ;
message_size-指向变量的指针,该变量用于存储队列的每个消息中的UNSIGNED数据元素的数量。 如果队列支持可变长度消息,则此数字表示最大消息长度;
suspend_type-指向存储任务挂起类型的变量的指针。 有效值为NU_FIFONU_PRIORITY ;
task_waiting-指向变量的指针,该变量用于存储在此队列中挂起的任务数;
first_task-指向任务指针的指针,其中放置了第一个挂起的任务的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_QUEUE-无效的队列指针。

在Nucleus SE中调用队列信息
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Queue_Information(NUSE_QUEUE队列,ADDR *起始地址,U8 *队列大小,U8 *可用,U8 *消息,U8 *任务等待中,NUSE_TASK * first_task);

参数:

queue-请求有关信息的队列的索引;
start_address-指向ADDR类型变量的指针,其中将存储队列数据区域的开始地址;
queue_size-指向类型为U8的变量的指针,该变量将存储队列中可容纳的消息总数;
可用 -指向类型为U8的变量的指针,该变量将存储队列中的空闲位置数;
messages-指向类型为U8的变量的指针,该变量将在队列中存储当前消息数;
task_waiting-指向一个变量的指针,该变量将存储在此队列中挂起的任务数(如果禁用了任务锁定, 则不返回任何内容);
first_task-指向NUSE_TASK类型的变量的指针,将在其中存储第一个挂起任务的索引(如果禁用了任务锁定, 则不返回任何内容)。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_QUEUE-无效的队列索引;
NUSE_INVALID_POINTER-一个或多个指针参数不正确。

在Nucleus SE中实现队列信息显示

实现此API调用非常简单:

 *start_address = NUSE_Queue_Data[queue]; *queue_size = NUSE_Queue_Size[queue]; *available = NUSE_Queue_Size[queue] - NUSE_Queue_Items[queue]; *messages = NUSE_Queue_Items[queue]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Queue_Blocking_Count[queue]; if (NUSE_Queue_Blocking_Count[queue] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_QUEUE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == queue)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif 

该函数返回队列的状态。 然后,如果激活了任务锁定,则将返回待处理任务的数量和它们中第一个的索引(否则,两个参数都将设置为0)。

获取队列数


该实用程序调用返回在应用程序中配置的队列数。 在Nucleus RTOS中,它们的数量可能会随着时间变化,并且返回值将指示当前队列的数量。 在Nucleus SE中,返回值是在构建阶段设置的,无法更改。

在Nucleus RTOS中调用队列计数器
服务电话原型:
UNSIGNED NU_Established_Queues(VOID);

参数:
缺席。

返回值:
在系统中创建的队列数。

在Nucleus SE中调用队列计数器
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:
U8 NUSE_Queue_Count(无效);

参数:
缺席。

返回值:
在应用程序中配置的队列数。

在Nucleus SE中实现队列计数器
此API调用的实现非常简单: 返回 #define NUSE_QUEUE_NUMBER符号的值。

资料结构


队列使用五或六个数据结构(位于RAM或ROM中),这些数据结构是表的集合(与其他Nucleus SE对象类似),其数量和大小与应用程序中的队列数量和所选参数相对应。

RAM中的内核数据


该数据具有以下结构:

NUSE_Queue_Head []U8类型的指针数组,每个配置的队列都有一个条目,并指向消息队列的头部。 用作NUSE_Queue_Data []中的地址索引(见下文);
NUSE_Queue_Tail []U8类型的数组,在应用程序中配置的每个队列都有一个条目,并指向消息队列的尾部。 用作NUSE_Queue_Data []中的地址索引(见下文);
NUSE_Queue_Items []U8类型的数组,每个配置的队列都有一个条目,并且是队列中消息的计数器。 由于可以通过队列开始和结束的索引获得这些值,因此可以认为这些数据是冗余的,但是,存储计数器可以简化代码。
NUSE_Queue_Blocking_Count [] -此类型为U8的数组包含每个队列上挂起的任务数的计数器。 仅当激活了任务锁支持时才创建此数组。

当Nucleus SE启动时,这些数据结构通过NUSE_Init_Queue()函数初始化为零。 这是合乎逻辑的,因为所有队列都创建为空(不使用)。

以下是nuse_init.c文件中这些结构的定义:

 RAM U8 NUSE_Queue_Head[NUSE_QUEUE_NUMBER]; RAM U8 NUSE_Queue_Tail[NUSE_QUEUE_NUMBER]; RAM U8 NUSE_Queue_Items[NUSE_QUEUE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Queue_Blocking_Count[NUSE_QUEUE_NUMBER]; #endif 

RAM用户数据


用户负责提供用于存储每个队列的RAM区域。 该区域的大小应包含一个ADDR类型的数组,其中每个记录对应于队列中的一条消息。

ROM数据


该数据具有以下结构:

NUSE_Queue_Data [] -类型为ADDR的数组,每个配置的队列都有一个条目,并指向该队列的数据区域(请参阅用户RAM数据)。
NUSE_Queue_Size []U8类型的数组,每个配置的队列都有一个条目,并显示每个队列可以接收的最大消息数。

这些数据结构在nuse_config.c文件中声明和初始化(静态):

 ROM ADDR *NUSE_Queue_Data[NUSE_QUEUE_NUMBER] = { /* addresses of queue data areas ------ */ }; ROM U8 NUSE_Queue_Size[NUSE_QUEUE_NUMBER] = { /* queue sizes ------ */ }; 

队列的内存量


与所有Nucleus SE内核对象一样,队列所需的内存量很容易预测。

可以按以下方式计算应用程序中所有队列的ROM中的数据量(以字节为单位):
NUSE_QUEUE_NUMBER *(sizeof(ADDR)+1)

具有激活的任务锁定的应用程序中所有队列在RAM中的内核数据量(以字节为单位)如下计算:
NUSE_QUEUE_NUMBER * 3

如果禁用了锁定:
NUSE_QUEUE_NUMBER * 4

具有队列索引的队列在RAM中的用户数据量(以字节为单位):
NUSE_Queue_Size [队列] * sizeof(ADDR)

未实现的API调用


Nucleus SE中未实现可以在Nucleus RTOS中找到的四个API调用:

队列创建


该API调用创建一个队列;在Nucleus SE中,这是不必要的,因为队列是静态创建的。

服务电话原型:
状态NU_Create_Queue(NU_QUEUE *队列,char *名称,VOID *起始地址,UNSIGNED queue_size,OPTION message_type,UNSIGNED message_size,OPTIONsuspend_type);

参数:

队列 -指向用户提供的控制单元的指针;它用于管理其他API调用中的队列;
name-指向一个由七个字符组成的队列名称的指针,该名称带有一个空终止字节;
start_address-队列开始的地址;
message_type-队列支持的消息类型。 它可以采用值NU_FIXED_SIZENU_VARIABLE_SIZE ;
message_size-如果队列支持固定长度的消息,则此参数设置每个消息的确切长度,否则,如果队列支持可变长度的消息,则此值为最大消息长度;
pause_type-确定队列中挂起任务的类型。 它可以采用值NU_FIFONU_PRIORITY ,分别表示FIFO原则(先进先出)或任务暂停的优先级原则。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_QUEUE-指向队列控制块的空指针( NULL ),或者该指针已在使用中;
NU_INVALID_MEMORY-start_address中指定的无效内存区域;
NU_INVALID_MESSAGE-无效的message_type参数;
NU_INVALID_SIZE-队列不支持该长度的消息,或者队列大小和/或消息长度为0;
NU_INVALID_SUSPEND-无效的suspend_type参数。

删除队列


此API调用将删除先前创建的队列。 Nucleus SE不需要此功能,因为队列是静态创建的,无法删除。

服务电话原型:
状态NU_Delete_Queue(NU_QUEUE *队列);

参数:
queue-队列控制块的指针。

返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_QUEUE-无效的队列指针。

队列指针


此API调用将建立指向系统中所有队列的指针的顺序列表。 Nucleus SE不需要这样做,因为使用简单索引而不是指针来标识队列。

服务电话原型:
UNSIGNED NU_Queue_Pointers(NU_QUEUE **指针列表,UNSIGNED maximum_pointers);

参数:
pointer_list-指针数组NU_QUEUE的指针。 该数组将填充指向系统中创建的队列的指针。
maximum_pointers-数组中的最大指针数。

返回值:
数组中NU_QUEUE指针的数量。

排队(广播到队列)


此API调用将消息传递到队列中挂起的所有等待来自指定队列的消息的任务。 Nucleus SE未实现此功能,因为它增加了冗余。

服务电话原型:
状态NU_Broadcast_To_Queue(NU_QUEUE *队列,VOID *消息,UNSIGNED大小,UNSIGNED挂起);

参数:

queue-队列控制块的指针;
message-指向已发送消息的指针;
size-消息中UNSIGNED元素的数量。 如果队列支持可变长度消息,则此参数必须等于或小于队列支持的消息长度。 如果队列支持固定长度的消息,则此参数必须等于队列支持的消息的长度;
suspen-指示如果队列已满,是否暂停调用任务。 它可以是NU_NO_SUSPENDNU_SUSPEND或超时值。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_QUEUE-无效的队列指针;
NU_INVALID_POINTER-消息的空指针( NULL );
NU_INVALID_SIZE-指定的消息长度与创建队列时指定的长度不兼容;
NU_INVALID_SUSPEND-尝试从与任务无关的线程中暂停任务;
NU_QUEUE_FULL-队列中没有足够的空间来存储消息;
NU_TIMEOUT-超时到期后,队列仍然满;
NU_QUEUE_DELETED-任务挂起时删除队列;
NU_QUEUE_RESET-任务挂起时重置了队列。

兼容Nucleus RTOS


与所有其他Nucleus SE对象一样,我的目标是最大程度地提高与Nucleus RTOS的应用程序代码兼容性。 队列也不例外,从用户的角度来看,它们的实现方式与Nucleus RTOS中的实现方式相同。 由于存在一定的不兼容性,因此我认为这是可以接受的,因为这样一来,就所需的内存量而言,代码将变得更加易于理解和效率更高。 否则,Nucleus RTOS API调用几乎可以直接移植到Nucleus SE。

对象标识符


在Nucleus RTOS中,所有对象均由具有特定数据类型的数据结构(控制单元)描述。 指向该控制单元的指针用作队列的标识符。 我决定在Nucleus SE中,需要一种不同的方法来有效地使用内存:所有内核对象都由RAM和/或ROM中的一组表描述。 这些表的大小由每种类型的已配置对象的数量确定。 特定对象的标识符是此表中的索引。 因此,我将NUSE_QUEUE定义为与U8等效,此类型的变量(而非指针)用作队列标识符。 如果将代码从Nucleus SE移植到Nucleus RTOS,反之亦然,则这种轻微的不兼容性很容易处理。 通常,除了移动和存储之外,不对对象标识符执行任何操作。

Nucleus RTOS还支持队列命名。 这些名称仅用于调试。 我从Nucleus SE中排除了它们以节省内存。

邮件大小和类型


在Nucleus RTOS中,可以将队列配置为处理由任意数量的未签名元素组成的消息。 在Nucleus SE中,队列得到了简化,仅支持单个ADDR类型的消息。 Nucleus SE的数据通道更加灵活,在某些情况下可以替代队列。 本系列的下两篇文章将介绍频道。

Nucleus SE还支持可变长度消息队列,该队列仅指定创建过程中的最大消息长度。 Nucleus SE不支持可变的消息长度

队列大小


在Nucleus SE中,队列中的最大消息数为256,因为所有变量和常量均为U8类型。 Nucleus RTOS没有这样的限制。

未实现的API调用


Nucleus RTOS支持十个队列管理调用。 其中有四个未在Nucleus SE中实现。 这些调用的详细信息以及做出此决定的原因,可以在上文“未实现的API调用”部分中找到。

下一篇文章将讨论数据传输通道。

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

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


All Articles