2

我正在用 C 编写一个嵌入式控制系统,该系统由多个相互发送消息的任务组成(我相信这是一个相当常见的习语!),但我很难设计一种机制:

  • 很整洁
  • 是通用的
  • 是相对有效的
  • 最重要的是:独立于平台(具体来说,不违反严格混叠或对齐问题)

从概念上讲,我想将每种消息类型表示为一个单独的结构定义,并且我想要一个具有以下功能(简化)的系统:

void sendMsg(queue_t *pQueue, void *pMsg, size_t size);
void *dequeueMsg(queue_t *pQueue);

其中 aqueue_t包含一个节点的链接列表,每个节点都有一个char buf[MAX_SIZE]字段。我所在的系统没有malloc()实现,因此需要一个全局空闲节点池,然后是以下之一(以粗体表示的感知问题):

  1. sendMsg()memcpy传入消息的一部分放入空闲节点的缓冲区中。我的理解是,除非调用者对返回值进行进一步
    处理,否则这将存在对齐问题。dequeueMsg()memcpy
  2. 或者会有一个void *getFreeBuffer()返回buf[]下一个空闲节点的函数,调用者(发送者)将把它转换为适当的指针类型。
    我的理解是,现在这将在进入的过程中出现对齐问题,并且仍然需要稍后才能避免memcpydequeueMsg()退出时出现对齐问题。
  3. 或将节点中的缓冲区重新定义queue_t为 (eg) uint32_t buf[MAX_SIZE]
    我的理解是这违反了严格的别名,并且不是独立于平台的。

我能看到的唯一其他选择是创建所有消息类型的联合以及char buf[MAX_SIZE],但我不认为这是“整洁”!

所以我的问题是,如何正确地做到这一点?

4

2 回答 2

2

我们处理这个问题的方法是让我们的空闲列表完全由对齐的节点组成。事实上,对于不同大小的节点,我们有多个空闲列表,因此我们有在 2 字节、4 字节和 16 字节边界上对齐的列表(我们的平台不关心大于一个 SIMD 向量的对齐)。任何分配都会四舍五入到其中一个值并放入正确对齐的节点中。因此,sendMsg 总是将其数据复制到对齐的节点中。由于您自己编写空闲列表,因此您可以轻松地强制对齐。

我们还将使用 #pragma 或 declspec 强制该 char buf[MAX_SIZE] 数组与 queue_t 节点结构内的至少一个字边界对齐。

这当然假设输入数据是对齐的,但是如果由于某种原因您传递的消息预计(假设)偏离对齐 3 个字节,您始终可以使用模数检测到并返回偏移量自由节点。

通过该底层设计,我们拥有支持上述选项 1 和 2 的接口。同样,我们的前提条件是输入数据总是原生对齐的,所以我们的出队当然会返回一个对齐的指针;但是,如果您需要奇怪对齐的数据,再次,只需偏移到空闲节点并返回偏移指针。

这使您可以处理 void *s 从而避免您的严格混叠问题。(总的来说,尽管我认为在编写自己的内存分配器时可能需要放宽严格的别名要求,因为它们本质上会在内部模糊类型。)

于 2009-08-17T22:59:20.160 回答
1

我不明白为什么1会出现对齐问题 - 只要每个buf[MAX_SIZE]元素都与消息结构中出现的自然最大的单个原始类型(可能是 32 位或 64 位)对齐,那么每个元素的内容无关紧要消息类型是;因为它将始终与该大小对齐。

编辑

实际上,它甚至比这更简单。由于消息队列中的每个元素都有MAX_SIZE长度,因此假设您以自己的方式开始每条消息buf(即,如果消息 < MAX_SIZE,则不打包它们),那么队列中的每条消息都将至少在边界上开始和它本身一样大,因此它总是会正确对齐。

于 2009-08-17T22:58:38.463 回答