内核消息队列

更新时间:
2024-03-14
下载文档

内核消息队列

消息队列是一个可以存放多则消息的 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.
文档内容是否对您有所帮助?
有帮助
没帮助