多处理器数据通信的结构体打包

3
在我们的ASIC上有两个处理器,这意味着有两个稍微不同的编译器。我们通过结构体在两者之间传递数据以进行通信,这种情况每秒钟经常发生,所以我们没有太多时间来浪费。
问题在于两个编译器对填充的处理方式不同。因此,当我们从一个域复制数据到另一个域时,我们得到了不正确对齐的值。我们最初的解决方案是在结构体内部的所有内容上加上"attribute((packed))"。虽然这似乎大多数时候都有效,但它并不具备可移植性。随着我们将代码移植到不同平台,我注意到并非所有编译器都理解"attribute((packed))",我希望保持我们的代码可移植性。
这里有人遇到过这种问题吗?你会推荐什么?
提前感谢!

1
别忘了字节序问题。压缩的思想应该是可移植的,但语法则可能不太一样。 - Chris Stratton
3个回答

3
我会手动打包这样的结构,以便任何现在或未来的编译器都不会出现填充问题。虽然这很繁琐,但它是一种可移植的、未来可靠的解决方案,值得付出努力。

2

C语言中的结构体排列基本上是不可移植的,因此使用__attribute__((packed))或类似方法通常是强制固定结构体布局的典型方式。

另一种选择是在适当的位置手动添加pad字段,并了解每个平台的对齐约束条件,以确保两个平台上的两个结构体匹配 - 但这本质上是手动的attribute((packed))

请注意,dwarves工具包中的pahole实用程序(公告论文)是一个很好的工具,可以检查和查看结构的对齐方式,假设您的编译器发出ELF文件。


谢谢你关于pahole的建议,我自己可能永远都不会发现这样的工具。我想也许我会写一个脚本来解析我的两个elf文件的输出,并验证它们的对齐是否一致。 - Hart Thomson

0
这就是为什么你不应该使用结构体来进行数据协议的原因。虽然这样说可能有些严厉,但结构体很不幸地是不可移植的,因此是危险的。你可以考虑像这样做(伪代码):
typedef struct
{
  uint32_t x;
  uint16_t y;
  ...
} my_struct_t;  // some custom struct

#define PROTOCOL_SIZE ( sizeof(uint32_t) + sizeof(uint16_t) + ... )


void pack (uint8_t raw_data[PROTOCOL_SIZE], 
           const my_struct_t* ms)
{
  uint16_t i=0;

  memcpy(&raw_data[i], ms->x, sizeof(ms->x));
  i += sizeof(ms->x);

  memcpy(&raw_data[i], ms->y, sizeof(ms->y));
  i += sizeof(ms->y);

  ...
}

然后,编写一个类似的unpack()函数,将原始数据复制到结构体中。
这样做的好处是:100%可移植。如果协议指定了特定的字节序,此函数也可以处理该转换(无论如何您都必须要做)。
缺点是需要额外的内存缓冲区和一些额外的数据复制。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接