我正在用 C 语言编写一个嵌入式控制系统,该系统由多个相互发送消息的任务组成(我相信这是一个相当常见的习惯用法!),但我很难设计一种机制:
- 很整洁
- 是通用的
- 相对高效
- 最重要的是:独立于平台(具体来说,不违反严格别名或对齐问题)
从概念上讲,我想将每种消息类型表示为单独的结构定义,并且我想要一个具有以下功能(简化)的系统:
void sendMsg(queue_t *pQueue, void *pMsg, size_t size);
void *dequeueMsg(queue_t *pQueue);
其中 queue_t
包含一个节点链表,每个节点都有一个 char buf[MAX_SIZE]
field 。我所在的系统没有 malloc()
实现,因此需要一个全局的空闲节点池,然后是以下之一(感知到的问题以粗体显示):
-
sendMsg()
做memcpy
将传入消息放入空闲节点的缓冲区中。
我的理解是,这将存在对齐问题,除非dequeueMsg()
的调用者进一步memcpy
关于返回值。 - 或者会有一个
void *getFreeBuffer()
返回buf[]
的函数下一个空闲节点的,调用者(发送者)将其转换为适当的类型指针。
我的理解是,这现在在进入过程中会出现对齐问题,并且仍然需要memcpy
之后dequeueMsg()
以避免退出时出现对齐问题。 - 或重新定义
queue_t
中的缓冲区节点为(例如)uint32_t buf[MAX_SIZE]
.
我的理解是,这违反了严格的别名,并且不独立于平台。
我能看到的唯一其他选项是创建所有消息类型的 union 以及 char buf[MAX_SIZE]
,但我不认为这是“整洁的”!
所以我的问题是,如何正确地做到这一点?
最佳答案
我们处理这个问题的方法是让我们的空闲列表完全由对齐的节点组成。事实上,我们对于不同大小的节点有多个空闲列表,因此我们有在 2 字节、4 字节和 16 字节边界上对齐的列表(我们的平台不关心大于一个 SIMD vector 的对齐)。任何分配都会四舍五入到这些值之一,并放入正确对齐的节点中。因此,sendMsg 总是将其数据复制到对齐的节点中。由于您自己编写空闲列表,因此可以轻松强制对齐。
我们还将使用 #pragma 或 declspec 强制 char buf[MAX_SIZE] 数组与queue_t 节点结构内的至少一个字边界对齐。
当然,这假设输入数据已对齐,但如果由于某种原因您传递的消息预计与对齐相差(比方说)3个字节,那么您始终可以用模数检测它并返回空闲节点的偏移量。
通过该底层设计,我们拥有支持上述选项 1 和 2 的接口(interface)。同样,我们的前提条件是输入数据始终是 native 对齐的,因此我们的出队当然会返回一个对齐的指针;但如果您需要奇怪对齐的数据,只需偏移到空闲节点并返回偏移指针即可。
这使您可以处理 void *s,从而避免严格的别名问题。 (一般来说,我认为在编写自己的内存分配器时,您可能需要放松严格的别名要求,因为本质上它们会在内部模糊类型。)
关于c - C 语言的消息调度系统不会破坏严格的别名和对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1290834/