2

我正在尝试构建一个简单的自定义应用层协议,本质上带有时间戳以及一些其他有用的信息,以便在不同的 Linux 系统之间执行少量网络测量。

就 Linux 系统而言,在我最初的想法中,实现应该尽可能地在不同平台(x86、ARM、...)之间移植。

为了管理标题,我创建了这个结构:

struct myhdr {
    __u8 reserved; // 1 byte
    __u8 ctrl; // 1 byte
    __u16 id; // 2 bytes
    __u16 seq; // 2 bytes
    __u16 len; // 2 bytes
    struct timeval sendtime; // 8 or 16 bytes
};

之后,一些有效载荷数据可能存在也可能不存在(如果 len=0)。由于这些数据必须通过网络发送,如果我没记错的话,我需要要打包的结构,没有任何对齐填充。

我的疑问实际上是这是否可以被认为是已经打包的,主要是由于存在struct timeval, 来携带时间戳。

在 32 位系统下,struct timeval应该是 8 个字节。在 64 位系统下,它应该是 16 字节(刚刚通过打印测试过sizeof(struct timeval))。

在 32 位系统下,假设它已经打包是否安全,因为 1+1+2+2+2 字节 = 8 字节,即 的大小sendtime?或者在任何情况下都会添加一个填充,以便将每个字段与最后一个字段对齐,哪个是最大的?

那么在最后一个字段是 16 字节的 64 位系统中会发生什么?我认为在任何情况下,结构都不会再“被布局打包”了(这是正确的吗?)。

在为不同平台编译代码时,添加是否__attribute__((packed))足够且始终有必要确保结构被打包?有更好的解决方案吗?

4

1 回答 1

5

如果你定义一个有线协议,你真的需要使用你自己的类型。为了安全起见,从 1970 年开始,您应该使用 64 位来表示秒数,即使许多 32 位系统仍然使用 32 位计数器。

struct timeval来自系统标头,基本上可以有任何大小,从 8 个字节开始。我确定那里有 32 位系统,它的大小为 12(64 位time_t用于tv_sec避免 Y2038 问题,32 位long/suseconds_t用于tv_usec)。此外,POSIX 只要求struct timeval具有某些成员。一些系统在其系统头文件中有显式字段以避免编译器的隐式填充,从而导致(假设的)打包行为的进一步差异。

虽然__attribute__ ((pack))不递归应用(所以sizeof (p->sendtime)equal sizeof (struct timeval),它仍然将整个结构(包括sendtime成员)的对齐减少到 1,因此成员可能未对齐并且不适合直接与期望 a 的函数一起使用struct timeval *

于 2019-01-13T17:19:44.020 回答