关于RTOS的全部真相。 第20条 信号量:辅助服务和数据结构



本文继续对信号量进行回顾。

助手信号灯服务


Nucleus RTOS具有四个提供信号灯相关功能的API调用:重置信号灯,检索信号灯信息,检索应用程序中信号灯的数量以及检索指向应用程序中所有信号灯的指针。 其中的前三个在Nucleus SE中实现。

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

重置信号量


此API调用将信号量重置为其初始的未使用状态。 与其他内核对象的功能相比,此API函数是不同寻常的,因为尽管它执行了重置操作,但它不仅将计数器设置为初始值,而且在调用中传递了新的初始计数器值。 在信号量上暂停的所有任务都将恢复并返回Nucleus SE和Nucleus RTOS中的NUSE_SEMAPHORE_WAS_RESET代码NU_SEMAPHORE_RESET

调用以重置Nucleus RTOS中的信号量

服务电话原型:

状态NU_Reset_Semaphore(NU_SEMAPHORE *信号量,UNSIGNED initial_count);

参数:

信号量 -指向用户提供的信号量控制块的指针;
initial_count-将信号量设置为的值。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_SEMAPHORE-无效的信号量指针。

调用以重置Nucleus SE中的信号量
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Semaphore_Reset(NUSE_SEMAPHORE信号量,U8 initial_count);

参数:

信号量 -转储信号量的索引(ID);
initial_count-将信号量设置为的值。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_SEMAPHORE-无效的信号量索引。

在Nucleus SE中实现信号量重置

NUSE_Semaphore_Reset() API函数的主要任务是将相应的NUSE_Semaphore_Counter []元素设置为指定值(在检查参数之后)。

如果激活了任务锁定,则需要以下代码来解锁任务:

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

信号灯上挂起的每个任务都标记为“完成”,并且任务挂起代码返回NUSE_SEMAPHORE_WAS_RESET 。 在完成此过程之后,如果使用优先级调度程序,则该调用将初始化NUSE_Reschedule() ,因为优先级较高的一个或多个任务可能进入就绪状态并等待恢复。

信号量信息


该实用程序调用返回信号量信息。 Nucleus SE中此调用的实现与Nucleus RTOS的不同之处在于,返回的信息较少,因为不支持对象命名和暂停顺序,并且可以禁用任务挂起本身。

在Nucleus RTOS中要求信号灯信息

服务电话原型:

状态NU_Semaphore_Information(NU_SEMAPHORE *信号量,CHAR *名称,UNSIGNED * current_count,OPTION * suspend_type,UNSIGNED * task_waiting,NU_TASK ** first_task);

参数:

信号量 -指向需要有关其信息的信号量控制块的指针;
name-指向信号量的8个字符的名称的指针,此区域中包含零个终止字节;
current_count-指向将使用信号量计数器当前值的变量的指针;
pause_type-指向将接受暂停任务类型的变量的指针,可以采用NU_FIFONU_PRIORITY值;
task_waiting-指向变量的指针,该变量将获取信号量中已挂起任务的数量;
first_task-指向类型为NU_TASK的变量的指针,它将采用指向第一个挂起任务的控制单元的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_SEMAPHORE-无效的信号量指针。

在Nucleus SE中要求信号量信息
该API调用支持Nucleus RTOS API的核心功能。

服务电话原型:

状态NUSE_Semaphore_Information(NUSE_SEMAPHORE信号量,U8 *当前计数,U8 *任务等待中,NUSE_TASK *第一任务);

参数:

信号量 -要求提供有关信息的信号的索引;
current_count-指向将使用信号量计数器当前值的变量的指针;
task_waiting-指向一个变量的指针,该变量将接收此信号量上已挂起的任务数(如果禁用了对挂起任务的支持,则不会返回任何内容);
first_task-指向类型为NUSE_TASK的变量的指针,该变量将获取第一个已暂停任务的索引(如果禁用了对暂停任务的支持,则不会返回任何内容)。

返回值:

NUSE_SUCCESS-呼叫已成功完成;
NUSE_INVALID_SEMAPHORE-无效的信号量索引;
NUSE_INVALID_POINTER-一个或多个指针参数不正确。

在Nucleus SE中实现信号量信息

实现此API调用非常简单:

 NUSE_CS_Enter(); *current_count = NUSE_Semaphore_Counter[semaphore]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Semaphore_Blocking_Count[semaphore]; if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

该函数返回信号量的状态。 然后,如果激活了API调用阻止功能,则返回待处理任务的数量和第一个任务的索引(否则,这些参数设置为0)。

获取信号量


该实用程序调用返回应用程序中的信号量。 在Nucleus RTOS中,该值随时间变化,返回值对应于当前的信号量;在Nucleus SE中,返回值在组装阶段设置,并且不再更改。

在Nucleus RTOS中调用信号量计数器

服务电话原型:

UNSIGNED NU_Established_Semaphores(VOID);

参数:
缺席。

返回值:
在应用程序中创建的信号灯数量。

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

服务电话原型:

U8 NUSE_Semaphore_Count(无效);

参数:
缺席。

返回值:
在应用程序中配置的信号灯数量。

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

资料结构


信号量使用两个或三个数据结构数组(在RAM和ROM中),与所有其他Nucleus SE对象一样,它们是一组表,表的大小取决于应用程序中信号量的数量和所选参数。

我强烈建议应用程序代码不要直接访问这些数据结构,而是通过提供的API函数引用它们。 这将避免与Nucleus SE的未来版本不兼容以及不必要的副作用,并简化了将应用程序移植到Nucleus RTOS的过程。 为了更好地理解服务调用代码的工作方式以及进行调试,下面将详细介绍数据结构。

RAM数据


该数据具有以下结构:
NUSE_Semaphore_Counter []-U8类型的数组,每个配置的信号量都有一个条目,它存储计数器的值。
NUSE_Semaphore_Blocking_Count []-U8类型的数组,包含在每个信号量上阻塞的任务计数器。 仅当激活了API调用阻止功能时,此数组才存在。
Ncleus SE启动时, NUSE_Semaphore_Counter []初始化为初始值(请参见下面的“ ROM中的数据”),并使用NUSE_Init_Semaphore()重置 NUSE_Semaphore_Blocking_Count [] 。 以下文章之一将提供对Nucleus SE启动过程的完整描述。

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

 RAM U8 NUSE_Semaphore_Counter[NUSE_SEMAPHORE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Semaphore_Blocking_Count[NUSE_SEMAPHORE_NUMBER]; #endif 

ROM数据


资料结构:
NUSE_Semaphore_Initial_Value [] -类型为U8的数组,每个信号量都有一个记录,这些是信号量的初始值。

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

 ROM U8 NUSE_Semaphore_Initial_Value[NUSE_SEMAPHORE_NUMBER] = { /* semaphore initial count values */ }; 

信号量的内存量


像所有Nucleus SE内核对象一样,信号量所需的数据量是可以预测的。

应用程序中所有信号量的ROM内存量(字节)为NUSE_SEMAPHORE_NUMBER

可以通过以下方式计算应用程序中所有信号量(激活了对锁API的调用)后在RAM中的内存量(以字节为单位):
NUSE_SEMAPHORE_NUMBER * 2

否则为NUSE_SEMAPHORE_NUMBER

未实现的API调用


Nucleus SE中未实现对Nucleus RTOS中存在的信号量的三个API调用。

创建信号量


此API调用将创建一个信号灯。 不需要Nucleus SE,因为信号是静态创建的。

服务电话原型:
状态NU_Create_Semaphore(NU_SEMAPHORE *信号量,CHAR *名称,UNSIGNED initial_count,OPTIONsuspend_type);

参数:

信号量 -指向用户提供的信号量控制块的指针;它用于控制其他API调用中的信号量;
name-指向8个字符的信号灯名称的指针,带有终止的空字节;
initial_count-信号量的初始值;
suspend_type-指示暂停信号量上的任务的原理。 它可以采用值NU_FIFONU_PRIORITY ,这与FIFO的原理(先进先出)和任务暂停的优先级相对应。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_SEMAPHORE-表示信号量控制块的指针为NULL或已被使用;
NU_INVALID_SUSPEND-无效的suspend_type参数。

信号量去除


此API调用将删除先前创建的信号灯。 不需要Nucleus SE,因为信号是静态创建的,无法删除。

服务电话原型:

状态NU_Delete_Semaphore(NU_SEMAPHORE *信号量);

参数:

信号量 -指向信号量控制块的指针。

返回值:

NU_SUCCESS-调用成功完成;
NU_INVALID_SEMAPHORE-无效的信号量指针。

信号量指针


该API调用形成了指向系统中所有信号量的指针的顺序列表。 它不需要Nucleus SE,因为信号量是由简单的索引而不是指针标识的。

服务电话原型:

UNSIGNED NU_Semaphore_Pointers(NU_SEMAPHORE **指针列表,UNSIGNED maximum_pointers);

参数:

pointer_list-指向NU_SEMAPHORE指针数组的指针,该数组充满了信号量的指针;
maximum_pointers-数组中的最大指针数。

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

兼容Nucleus RTOS


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

对象标识符


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

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

柜台尺寸


在Nucleus RTOS上,信号量计数器的类型为unsigned ,通常是32位变量。 Nucleus SE具有8位计数器,但是可以轻松更改。 通常,Nucleus RTOS不会检查信号量溢出。 调用Nucleus SE API不会为计数器分配大于255的值。

未实现的API调用


Nucleus RTOS支持使用信号灯的八个实用程序调用。 其中,三个在Nucleus SE中未实现。 上面已经描述了这些挑战的详细信息以及将其排除在Nucleus SE之外的决定。

下面的文章将检查邮箱。

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

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


All Articles