信号量:介绍和基本服务
查看 RTOS Revealed 系列
信号量在之前的文章中介绍过。它们的主要用途是控制对资源的访问。
使用信号量
在 Nucleus SE 中,信号量是在构建时配置的。一个应用程序最多可以配置 16 个信号量。如果未配置信号量,则应用程序中不包含属于信号量的数据结构或服务调用代码。
信号量只是一个 U8 类型的计数器 ,对其的访问受到控制,以便多个任务可以安全地使用它。一个任务可以减少(获得)一个信号量并增加(释放)它。尝试获取值为零的信号量可能会导致错误或任务暂停,具体取决于 API 调用中选择的选项和 Nucleus SE 配置。
配置信号量
信号量数量
与 Nucleus SE 的大多数方面一样,信号量的配置主要由 #define 控制 nuse_config.h 中的语句 .关键设置是 NUSE_SEMAPHORE_NUMBER ,它决定了为应用程序配置了多少信号量。默认设置为 0(即没有使用信号量),您可以将其设置为最多 16 的任何值。错误的值将导致编译时错误,这是由 nuse_config_check.h<中的测试生成的/b> (这包含在 nuse_config.c 中 因此使用此模块编译)导致 #error 正在编译的语句。
选择非零值是信号量的“主启用”。这会导致一些数据结构被相应地定义和调整大小,本文稍后会详细介绍。它还激活 API 启用设置。
API 启用
Nucleus SE 中的每个 API 函数(服务调用)都有一个启用 #define nuse_config.h 中的符号 .对于信号量,这些是:
NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT
默认情况下,所有这些都设置为 FALSE ,从而禁用每个服务调用并禁止包含任何实现代码。要为应用程序配置信号量,您需要选择要使用的 API 调用并将其启用符号设置为 TRUE .
这是默认 nuse_config.h 的摘录 文件。
#define NUSE_SEMAPHORE_NUMBER 0 /* 系统中信号量的数量 - 0-16 */#define NUSE_SEMAPHORE_OBTAIN FALSE /* 服务调用使能 */#define NUSE_SEMAPHORE_RELEASE FALSE /* 服务调用使能 */#define NUSE_SEMAPHORE_RESET调用使能 */#define NUSE_SEMAPHORE_INFORMATION FALSE /* 服务调用使能 */#define NUSE_SEMAPHORE_COUNT FALSE /* 服务调用使能 */
如果启用了信号量 API 功能并且未配置信号量(NUSE_Semaphore_Count() 除外),则会导致编译时错误 这总是被允许的)。如果您的代码使用未启用的 API 调用,则会导致链接时间错误,因为应用程序中不会包含任何实现代码。
信号量服务调用
Nucleus RTOS 支持八个属于信号量的服务调用,提供以下功能:
获取信号量。由 NUSE_Semaphore_Obtain() 实现 在 Nucleus SE 中。
释放信号量。由 NUSE_Semaphore_Release() 实现 在 Nucleus SE 中。
将信号量恢复到未使用状态,没有暂停(重置)任务。由 NUSE_Semaphore_Reset() 实现 在 Nucleus SE 中。
提供有关指定信号量的信息。由 NUSE_Semaphore_Information() 实现 在 Nucleus SE 中。
返回为应用程序(当前)配置了多少信号量的计数。由 NUSE_Semaphore_Count() 实现 在 Nucleus SE 中。
向应用程序添加一个新的信号量(创建)。未在 Nucleus SE 中实现。
从应用程序中移除一个信号量(删除)。未在 Nucleus SE 中实现。
返回指向应用程序中所有信号量(当前)的指针。未在 Nucleus SE 中实现。
详细检查了每个服务调用的实现。
信号量获取和释放服务
可以对信号量执行的基本操作是获取(递减)和释放(递增)它。 Nucleus RTOS 和 Nucleus SE 分别为这些操作提供了两个基本的 API 调用,这里将对其进行讨论。
获取信号量
用于获取信号量的 Nucleus RTOS API 调用非常灵活,如果操作无法立即完成,您可以无限期挂起或超时挂起;即,您尝试获取当前值为 0 的信号量。 Nucleus SE 提供相同的服务,只是任务挂起是可选的,并且没有实现超时。
用于获取信号量的 Nucleus RTOS API 调用
服务调用原型:
状态 NU_Obtain_Semaphore(NU_SEMAPHORE *semaphore,
未签名挂起);
参数:
信号量 – 指向用户提供的信号量控制块的指针
暂停 – 任务暂停规范;可能是 NU_NO_SUSPEND 或 NU_SUSPEND 或超时值
退货:
NU_SUCCESS – 通话成功
NU_UNAVAILABLE – 信号量的值为零
NU_INVALID_SEMAPHORE – 信号量指针无效
NU_INVALID_SUSPEND – 尝试从非任务挂起
NU_SEMAPHORE_WAS_RESET – 任务挂起时信号量被重置
用于获取信号量的 Nucleus SE API 调用
此 API 调用支持 Nucleus RTOS API 的关键功能。
服务调用原型:
STATUS NUSE_Semaphore_Obtain(NUSE_SEMAPHORE 信号量,
U8暂停);
参数:
信号量 – 要使用的信号量的索引(ID)
暂停 – 任务暂停规范;可能是 NUSE_NO_SUSPEND 或 NUSE_SUSPEND
退货:
NUSE_SUCCESS – 通话成功
NUSE_UNAVAILABLE – 信号量的值为零
NUSE_INVALID_SEMAPHORE – 信号量索引无效
NUSE_INVALID_SUSPEND – 尝试从非任务线程挂起或未启用阻塞 API 调用
NUSE_SEMAPHORE_WAS_RESET – 任务挂起时信号量被重置
Nucleus SE 实现获取信号量
NUSE_Semaphore_Obtain() 的大部分代码 API 函数 - 参数检查后 - 由条件编译选择,取决于是否启用了对阻塞(任务挂起)API 调用的支持。我们将在此处分别查看这两种变体。
如果没有开启阻塞,这个 API 调用的逻辑很简单:
if (NUSE_Semaphore_Counter[semaphore] !=0) /* 信号量可用 */{ NUSE_Semaphore_Counter[semaphore]--; return_value =NUSE_SUCCESS;}else /* 信号量不可用 */{ return_value =NUSE_UNAVAILABLE;}
测试信号量值,如果非零,则递减。
启用阻塞后,逻辑变得更加复杂:
do{ if (NUSE_Semaphore_Counter[semaphore] !=0) /* 信号量可用 */ { NUSE_Semaphore_Counter[semaphore]--; return_value =NUSE_SUCCESS;暂停 =NUSE_NO_SUSPEND; } else /* 信号量不可用 */ { if (suspend ==NUSE_NO_SUSPEND) { return_value =NUSE_UNAVAILABLE; } else { /* 阻塞任务 */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, 信号量 <<4) | NUSE_SEMAPHORE_SUSPEND); return_value =NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value !=NUSE_SUCCESS) { 暂停 =NUSE_NO_SUSPEND; } } }} while (suspend ==NUSE_SUSPEND);
对代码的一些解释可能有用:
代码包含在 do...while 中 循环,在参数 suspend 时继续 具有值 NUSE_SUSPEND .
如果信号量非零,则递减。 暂停 变量设置为 NUSE_NO_SUSPEND 并且 API 调用以 NUSE_SUCCESS 退出 .
如果信号量为零并且挂起 设置为 NUSE_NO_SUSPEND ,API 调用以 NUSE_UNAVAILBLE 退出 .如果挂起设置为 NUSE_SUSPEND ,任务暂停。返回时(即任务被唤醒时),如果返回值为 NUSE_SUCCESS ,表示任务被唤醒,因为信号量已经释放(相对于信号量的重置)代码循环回到顶部。
释放信号量
用于释放信号量的 Nucleus RTOS API 调用非常简单;信号量增加并报告成功。 Nucleus SE 提供相同的服务,只是执行了溢出检查。
用于释放信号量的 Nucleus RTOS API 调用
服务调用原型:
状态 NU_Release_Semaphore(NU_SEMAPHORE *semaphore);
参数:
信号量 – 指向用户提供的信号量控制块的指针
退货:
NU_SUCCESS – 通话成功
NU_INVALID_SEMAPHORE – 信号量指针无效
用于释放信号量的 Nucleus SE API 调用
此 API 调用支持 Nucleus RTOS API 的关键功能。
服务调用原型:
状态 NUSE_Semaphore_Release(NUSE_SEMAPHORE 信号量);
参数:
信号量 – 要释放的信号量的索引(ID)
退货:
NUSE_SUCCESS – 通话成功
NUSE_INVALID_SEMAPHORE – 信号量索引无效
NUSE_UNAVAILABLE – 信号量的值为 255 且不能递增
Nucleus SE 发布信号量的实现
NUSE_Semaphore_Release() 的初始代码 API 函数 - 参数检查后 - 是通用的,无论是否启用任务阻塞。检查信号量的值,如果小于 255,则递减。
如果启用了对阻塞(任务挂起)API 调用的支持,则通过条件编译选择更多代码:
NUSE_CS_Enter();if (NUSE_Semaphore_Counter[semaphore] <255){ NUSE_Semaphore_Counter[semaphore]++; return_value =NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] !=0) { U8 index; /* 检查一个任务是否被阻塞 */ /* 在这个信号量上 */ NUSE_Semaphore_Blocking_Count[semaphore]--; for (index=0; index如果有任何任务在这个信号量上被挂起,第一个就会被唤醒。
下一篇文章将介绍一些与事件标志组相关的其他 API 调用,以及相关的数据结构。
Colin Walls 在电子行业拥有超过 30 年的经验,主要致力于嵌入式软件。 Colin 经常出席会议和研讨会,并撰写了大量技术文章和两本有关嵌入式软件的书籍,他是 Mentor Embedded [Mentor Graphics 嵌入式软件部门] 的嵌入式软件技术专家,常驻英国。他的常规博客位于:http://blogs.mentor.com/colinwalls。他可以通过电子邮件联系到 [email protected]
嵌入式