内核消息队列
消息队列是一个可以存放多则消息的 FIFO(先入先出)队列。
SylixOS 消息队列支持紧急消息的发送,紧急消息直接插入到消息队列的首部,紧急消息将最先被处理,保证了某些异常情况下的安全。
一个 SylixOS 消息队列必须要调用 API_MsgQueueCreate 函数创建后才能使用,如果创建成功,API_MsgQueueCreate 函数将返回一个消息队列的句柄。
线程如果需要接收消息,可以调 API_MsgQueueReceive 函数,中断服务程序不能调用 API_MsgQueueReceive 函数接收消息,因为 API_MsgQueueReceive 函数在消息队列为空时会阻塞当前线程。
中断服务程序可以使用 API_MsgQueueTryReceive 函数尝试接收消息,API_MsgQueueTryReceive 函数在消息队列为空时将立即返回而不会阻塞当前的线程。
发送消息可以调用 API_MsgQueueSend 函数。
当一个消息队列使用完毕后(并确保以后也不再使用),应该调用 API_MsgQueueDelete 函数将其删除,SylixOS 会回收该消息队列占用的内核资源。
消息队列
消息队列的创建和删除
#include <SylixOS.h>
LW_OBJECT_HANDLE API_MsgQueueCreate (CPCHAR pcName,
ULONG ulMaxMsgCounter,
size_t stMaxMsgByteSize,
ULONG ulOption,
LW_OBJECT_ID *pulId);
函数 API_MsgQueueCreate 原型分析:
- 此函数成功返回消息队列句柄,失败返回 LW_HANDLE_INVALID 并设置错误号。
- 参数 pcName 是消息队列的名字。
- 参数 ulMaxMsgCounter 是消息队列可容纳的最大消息个数。
- 参数 stMaxMsgByteSize 是消息队列的单则消息的最大长度。
- 参数 ulOption 是消息队列的创建选项如下表所示。
- 输出参数 pulId 用于接收消息队列的 ID。
需要注意的是,消息队列中最大消息队列的最小值为 sizeof(size_t),也就是说创建消息队列的最小容量为 sizeof(size_t)个字节大小。
宏名 | 含义 |
---|---|
LW_OPTION_WAIT_PRIORITY | 按优先级顺序等待 |
LW_OPTION_WAIT_FIFO | 按先入先出顺序等待 |
LW_OPTION_OBJECT_GLOBAL | 全局对象 |
LW_OPTION_OBJECT_LOCAL | 本地对象 |
#include <SylixOS.h>
ULONG API_MsgQueueDelete (LW_OBJECT_HANDLE *pulId);
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 pulId 是消息队列的句柄。
接收消息
#include <SylixOS.h>
ULONG API_MsgQueueReceive (LW_OBJECT_HANDLE ulId,
PVOID pvMsgBuffer,
size_t stMaxByteSize,
size_t *pstMsgLen,
ULONG ulTimeout);
ULONG API_MsgQueueReceiveEx (LW_OBJECT_HANDLE ulId,
PVOID pvMsgBuffer,
size_t stMaxByteSize,
size_t *pstMsgLen,
ULONG ulTimeout,
ULONG ulOption);
ULONG API_MsgQueueTryReceive (LW_OBJECT_HANDLE ulId,
PVOID pvMsgBuffer,
size_t stMaxByteSize,
size_t *pstMsgLen);
以上三个函数原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 参数 pvMsgBuffer 指向用于接收消息的消息缓冲区(一个 void 类型的指针,可以指向任意类型)。
- 参数 stMaxByteSize 是消息缓冲区的长度。
- 输出参数 pstMsgLen 用于接收消息的长度。
- 参数 ulTimeout 是等待的超时时间,单位为时钟嘀嗒 Tick 。
- 参数 ulOption 是消息队列的接收选项,如下表所示。
宏名 | 含义 |
---|---|
LW_OPTION_NOERROR | 大于缓冲区的消息自动截断(默认为此选项) |
调用 API_MsgQueueReceive 函数将从 ulId 代表的消息队列中获得消息:
- 当队列中存在消息时,该函数将把消息复制到参数 pvMsgBuffer 指向的消息缓冲区,如果缓冲区长度大于消息长度,缓冲区剩余部分不做修改;如果缓冲区长度小于消息长度,消息将被截断并且没有任何错误返回。API_MsgQueueReceiveEx 函数提供了消息错误检查机制,当消息被截断时,该函数将返回错误号 E2BIG 。
- 当队列中不存在消息时,线程将被阻塞,如果设置了 ulTimeout 的超时值为 LW_OPTION_WAIT_INFINITE,线程将永远阻塞直到消息到来;如果 ulTimeout 的超时值不为 LW_OPTION_WAIT_INFINITE,线程将在指定的时间超时后自动唤醒线程。
发送消息
#include <SylixOS.h>
ULONG API_MsgQueueSend (LW_OBJECT_HANDLE ulId,
const PVOID pvMsgBuffer,
size_t stMsgLen);
ULONG API_MsgQueueSendEx (LW_OBJECT_HANDLE ulId,
const PVOID pvMsgBuffer,
size_t stMsgLen,
ULONG ulOption);
以上两个函数原型分析:
- 函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 参数 pvMsgBuffer 指向需要发送的消息缓冲区(一个 void 类型的指针,可以指向任意类型)。
- 参数 stMsgLen 是需要发送的消息的长度。
- 参数 ulOption 是消息的发送选项,如下表所示。
宏名 | 含义 |
---|---|
LW_OPTION_DEFAULT | 默认的选项 |
LW_OPTION_URGENT | 紧急消息发送 |
LW_OPTION_BROADCAST | 广播发送 |
如果使用 LW_OPTION_URGENT 选项,那么该消息将被插入到消息队列的首部。如果使用 LW_OPTION_BROADCAST 选项,那么该消息将被传递到每一个等待该消息队列的线程。
带延时的发送消息
#include <SylixOS.h>
ULONG API_MsgQueueSend2 (LW_OBJECT_HANDLE ulId,
const PVOID pvMsgBuffer,
size_t stMsgLen,
ULONG ulTimeout);
ULONG API_MsgQueueSendEx2 (LW_OBJECT_HANDLE ulId,
const PVOID pvMsgBuffer,
size_t stMsgLen,
ULONG ulTimeout,
ULONG ulOption);
以上两个函数原型分析:
- 函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 参数 pvMsgBuffer 指向需要发送的消息缓冲区(一个 void 类型的指针,可以指向任意类型)。
- 参数 stMsgLen 是需要发送的消息的长度。
- 参数 ulTimeout 是发送消息的延时等待时间。
- 参数 ulOption 是消息的发送选项,如上表所示。
以上两个函数与 API_MsgQueueSend 函数不同的参数传递中增加了 ulTimeout 参数,该参数表示发送消息带延时等待功能,这意味着,当发送的消息队列满时,发送消息将等待 ulTimeout 时间,如果超时时间到时消息队列仍然处于满状态消息将被丢弃,否则消息被成功发送。
消息队列的清除
#include <SylixOS.h>
ULONG API_MsgQueueClear (LW_OBJECT_HANDLE ulId);
函数 API_MsgQueueClear 原型分析:
- 此函数成功返回 ERROR_NONE,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
消息队列的清除意味着队列中的所有消息将被删除(消息丢弃,队列仍然有效),企图从中接收消息不会得到预期的结果。
释放等待消息队列的所有线程
#include <SylixOS.h>
ULONG API_MsgQueueFlush (LW_OBJECT_HANDLE ulId,
ULONG *pulThreadUnblockNum);
函数原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 输出 ulId 是消息队列的句柄。
- 输出参数 pulThreadUnblockNum 返回被解除阻塞的线程数,可以为 NULL 。
调用 API_MsgQueueFlush 函数将使所有阻塞在指定消息队列山的线程(包括发送和接收线程)就绪,这样避免了线程长时间阻塞的状态。
#include <SylixOS.h>
ULONG API_MsgQueueFlushSend (LW_OBJECT_HANDLE ulId,
ULONG *pulThreadUnblockNum);
函数 API_MsgQueueFlushSend 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 输出参数 pulThreadUnblockNum 返回被解除阻塞的线程数,可以为 NULL 。
调用 API_MsgQueueFlushSend 函数将使所有阻塞在指定消息队列上的发送线程就绪,这样避免了发送线程因为长时间发送不出去消息而长时间阻塞的状态。
#include <SylixOS.h>
ULONG API_MsgQueueFlushReceive (LW_OBJECT_HANDLE ulId,
ULONG *pulThreadUnblockNum);
函数 API_MsgQueueFlushReceive 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 输出参数 pulThreadUnblockNum 返回被解除阻塞的线程数,可以为 NULL 。
调用 API_MsgQueueFlushReceive 函数将使所有阻塞在指定消息队列上的接收线程就绪,这样避免了接收线程因为长时间接收不到消息而长时间阻塞的状态。
获得消息队列的状态
#include <SylixOS.h>
ULONG API_MsgQueueStatus (LW_OBJECT_HANDLE ulId,
ULONG *pulMaxMsgNum,
ULONG *pulCounter,
size_t *pstMsgLen,
ULONG *pulOption,
ULONG *pulThreadBlockNum);
ULONG API_MsgQueueStatusEx (LW_OBJECT_HANDLE ulId,
ULONG *pulMaxMsgNum,
ULONG *pulCounter,
size_t *pstMsgLen,
ULONG *pulOption,
ULONG *pulThreadBlockNum,
size_t *pstMaxMsgLen);
以上两个函数原型分析:
- 函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 输出参数 pulMaxMsgNum 用于接收消息队列可容纳的最大消息个数。
- 输出参数 pulCounter 用于接收消息队列当前消息的数目。
- 输出参数 pstMsgLen 用于接收消息队列最近一则消息的长度。
- 输出参数 pulOption 用于接收消息队列的创建选项。
- 输出参数 pulThreadBlockNum 用于接收当前阻塞在该消息队列的线程数。
- 输出参数 pstMaxMsgLen 用于接收消息队列的单则消息的最大长度。
获得消息队列的名字
#include <SylixOS.h>
ULONG API_MsgQueueGetName (LW_OBJECT_HANDLE ulId, PCHAR pcName);
函数 API_MsgQueueGetName 原型分析:
- 此函数成功返回 ERROR_NONE ,失败返回错误号。
- 参数 ulId 是消息队列的句柄。
- 输出参数 pcName 是计数型信号量的名字, pcName 应该指向一个大小为 LW_CFG_OBJECT_NAME_SIZE 的字符数组。
以下程序展示了 SylixOS 消息队列的使用,内核模块在装载时创建了两个不同优先级的线程和一个 SylixOS 消息队列,其中一个线程用于发送消息到消息队列,另一个线程则从消息队列中接收消息并打印。
#define __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <stdio.h>
#include <module.h>
#include <string.h>
LW_HANDLE _G_hThreadAId = LW_OBJECT_HANDLE_INVALID;
LW_HANDLE _G_hThreadBId = LW_OBJECT_HANDLE_INVALID;
static LW_HANDLE _G_hMsgQ;
static PVOID tTestA (PVOID pvArg)
{
INT iError;
CHAR cMsg[64];
size_t stLen;
while (1) {
iError = API_MsgQueueReceive(_G_hMsgQ,
cMsg,
sizeof(cMsg),
&stLen,
LW_OPTION_WAIT_INFINITE);
if (iError != ERROR_NONE) {
break;
}
printk("tTestA(): get a msg \"%s\"\n", cMsg);
}
return (LW_NULL);
}
static PVOID tTestB (PVOID pvArg)
{
INT iError;
INT iCount = 0;
CHAR cMsg[64];
size_t stLen;
while (1) {
sprintf(cMsg, "hello SylixOS %d", iCount);
stLen = strlen(cMsg) + 1;
iCount++;
iError = API_MsgQueueSend(_G_hMsgQ, cMsg, stLen);
if (iError != ERROR_NONE) {
break;
}
API_TimeSSleep(1);
}
return (LW_NULL);
}
VOID module_init (VOID)
{
printk("MsgQueue_module init!\n");
_G_hMsgQ = API_MsgQueueCreate("msgq",
10,
64,
LW_OPTION_WAIT_FIFO |
LW_OPTION_OBJECT_LOCAL,
LW_NULL);
if (_G_hMsgQ == LW_OBJECT_HANDLE_INVALID) {
printk("MsgQueue create failed.\n");
return;
}
_G_hThreadAId = API_ThreadCreate("t_testa", tTestA, LW_NULL, LW_NULL);
if (_G_hThreadAId == LW_OBJECT_HANDLE_INVALID) {
printk("t_testa create failed.\n");
return;
}
_G_hThreadBId = API_ThreadCreate("t_testb", tTestB, LW_NULL, LW_NULL);
if (_G_hThreadBId == LW_OBJECT_HANDLE_INVALID) {
printk("t_testb create failed.\n");
return;
}
}
VOID module_exit (VOID)
{
if (_G_hThreadAId != LW_OBJECT_HANDLE_INVALID) {
API_ThreadDelete(&_G_hThreadAId, LW_NULL);
}
if (_G_hThreadBId != LW_OBJECT_HANDLE_INVALID) {
API_ThreadDelete(&_G_hThreadBId, LW_NULL);
}
API_MsgQueueDelete(&_G_hMsgQ);
printk("MsgQueue_module exit!\n");
}
在 SylixOS Shell 下装载模块:
#insmod ./MsgQueue.ko
MsgQueue_module init!
module MsgQueue.ko register ok, handle: 0x13338f0
tTestA(): get a msg "hello SylixOS 0"
tTestA(): get a msg "hello SylixOS 1"
tTestA(): get a msg "hello SylixOS 2"
tTestA(): get a msg "hello SylixOS 3"
tTestA(): get a msg "hello SylixOS 4"
tTestA(): get a msg "hello SylixOS 5"
在 SylixOS Shell 下卸载模块:
#rmmod MsgQueue.ko
MsgQueue_module exit!
module /lib/modules/MsgQueue.ko unregister ok.