具有多种消息类型的嵌入式 RTOS 生产者和消费者
Embedded RTOS producer and consumer with many types of messages
许多 RTOS 消息传递示例显示生产者生成像 int32 这样简单的东西,而消费者读取它。这很简单,因为您总是知道队列(或其他消息传递缓冲区)的类型和大小。
考虑从 GPS 读取数据的 RTOS 线程的情况。 GPS 可能有多种类型的消息。您无法保证消息何时到达、消息的顺序或消息的大小。每条消息都会有很多字段,每条消息都可以表示为一个结构。
这个 GPS 处理生产者 RTOS 线程最好解析输入的数据流,然后将数据传递出去,以便消费者线程可以轻松读取 GPS 数据。
假定使用 FreeRTOS 或 CMSIS RTOS。如何最好地建立这种安排?将使用哪些原语?从根本上讲,我正在寻找一种干净的方法,将几种不同类型的不同大小的结构从生产者线程中传递出来,并在消费者中读取它们。
FreeRTOS 文档结合 QueueSets 讨论了该问题。 (参见 "Alternatives to Using Queue Sets" 部分中的 https://www.freertos.org/Pend-on-multiple-rtos-objects.html)。
基本上,这个想法是用一个枚举来识别缓冲区中不同类型的消息。
对于您的缓冲区类型,您可以结合使用枚举和结构联合。
struct A {uint32_t foo;};
struct B {uint8_t bar;};
enum T {AType, BType};
struct GenericMessage{
T type;
union{
struct A a;
struct B b;
};
};
那么你可以这样区分消息:
void handleGenericMessage(struct GenericMessage* msg){
switch(msg->type){
case AType:
handleA(msg->a);
break;
case BType:
handleB(msg->b);
break;
}
您可以考虑使用指向消息的指针队列,而不是消息队列本身。这样,无论消息如何,队列中的每个项目都是固定大小(指针的大小)。
该技术确实需要谨慎的资源管理,以确保消息在接收方完成处理之前不会被修改、删除或重复使用。您需要一个分配-入队-出队-释放机制。
例如,给定:
enum eMessageType
{ AType,
BType
};
struct GenericMessage
{
eMessageType type;
char payload[0] ; // Note GCC zero-length array extension
};
struct A { struct GenericMessage,
uint32_t foo;
};
struct B { struct GenericMessage,
uint8_t bar;
};
那么发件人可能有类似(伪代码)的东西:
struct A* messageA = allocateMessageA( AType, 0x12345678 ) ;
struct B* messageB = allocateMessageB{ BType, 0x12 } ;
sendGeneric( genericQ, messageA ) ;
sendGeneric( genericQ, messageB ) ;
接收者:
struct GenericMessage* message_ptr = receiveGeneric( genericQ ) ;
switch( message_ptr->type )
{
case AType:
{
struct A* = (struct A*)message_ptr ;
uint32_t payload = message_ptr->payload ;
...
deallocateMessageA( message_ptr ) ;
}
break;
case BType:
{
struct B* = (struct B*)message_ptr ;
uint8_t payload = message_ptr->payload ;
...
deallocateMessageB( message_ptr ) ;
}
}
allocation/deallocation 函数的详细信息我还没有定义。一种简单的方法是使用固定块内存池。如果您的 RTOS 不提供这些,一个简单的实现是在池中有指向消息的指针队列,每个消息类型一个 queue/pool。要进行分配,您只需通过从队列中取出一条消息来从相关消息池中获取一个指针,然后通过将指针返回到队列中来解除分配。
请注意,某些 RTOS 允许直接发送可变长度的消息 - 例如,在 embOS 中,您可以为队列分配内存池,并在发送时指定长度,而不是在创建队列时指定长度。然而,接收者必须能够接收到尽可能多的消息。
许多 RTOS 消息传递示例显示生产者生成像 int32 这样简单的东西,而消费者读取它。这很简单,因为您总是知道队列(或其他消息传递缓冲区)的类型和大小。
考虑从 GPS 读取数据的 RTOS 线程的情况。 GPS 可能有多种类型的消息。您无法保证消息何时到达、消息的顺序或消息的大小。每条消息都会有很多字段,每条消息都可以表示为一个结构。
这个 GPS 处理生产者 RTOS 线程最好解析输入的数据流,然后将数据传递出去,以便消费者线程可以轻松读取 GPS 数据。
假定使用 FreeRTOS 或 CMSIS RTOS。如何最好地建立这种安排?将使用哪些原语?从根本上讲,我正在寻找一种干净的方法,将几种不同类型的不同大小的结构从生产者线程中传递出来,并在消费者中读取它们。
FreeRTOS 文档结合 QueueSets 讨论了该问题。 (参见 "Alternatives to Using Queue Sets" 部分中的 https://www.freertos.org/Pend-on-multiple-rtos-objects.html)。 基本上,这个想法是用一个枚举来识别缓冲区中不同类型的消息。
对于您的缓冲区类型,您可以结合使用枚举和结构联合。
struct A {uint32_t foo;};
struct B {uint8_t bar;};
enum T {AType, BType};
struct GenericMessage{
T type;
union{
struct A a;
struct B b;
};
};
那么你可以这样区分消息:
void handleGenericMessage(struct GenericMessage* msg){
switch(msg->type){
case AType:
handleA(msg->a);
break;
case BType:
handleB(msg->b);
break;
}
您可以考虑使用指向消息的指针队列,而不是消息队列本身。这样,无论消息如何,队列中的每个项目都是固定大小(指针的大小)。
该技术确实需要谨慎的资源管理,以确保消息在接收方完成处理之前不会被修改、删除或重复使用。您需要一个分配-入队-出队-释放机制。
例如,给定:
enum eMessageType
{ AType,
BType
};
struct GenericMessage
{
eMessageType type;
char payload[0] ; // Note GCC zero-length array extension
};
struct A { struct GenericMessage,
uint32_t foo;
};
struct B { struct GenericMessage,
uint8_t bar;
};
那么发件人可能有类似(伪代码)的东西:
struct A* messageA = allocateMessageA( AType, 0x12345678 ) ;
struct B* messageB = allocateMessageB{ BType, 0x12 } ;
sendGeneric( genericQ, messageA ) ;
sendGeneric( genericQ, messageB ) ;
接收者:
struct GenericMessage* message_ptr = receiveGeneric( genericQ ) ;
switch( message_ptr->type )
{
case AType:
{
struct A* = (struct A*)message_ptr ;
uint32_t payload = message_ptr->payload ;
...
deallocateMessageA( message_ptr ) ;
}
break;
case BType:
{
struct B* = (struct B*)message_ptr ;
uint8_t payload = message_ptr->payload ;
...
deallocateMessageB( message_ptr ) ;
}
}
allocation/deallocation 函数的详细信息我还没有定义。一种简单的方法是使用固定块内存池。如果您的 RTOS 不提供这些,一个简单的实现是在池中有指向消息的指针队列,每个消息类型一个 queue/pool。要进行分配,您只需通过从队列中取出一条消息来从相关消息池中获取一个指针,然后通过将指针返回到队列中来解除分配。
请注意,某些 RTOS 允许直接发送可变长度的消息 - 例如,在 embOS 中,您可以为队列分配内存池,并在发送时指定长度,而不是在创建队列时指定长度。然而,接收者必须能够接收到尽可能多的消息。