为什么sockaddr_storage结构被定义为它所定义的方式?

7
以下是sockaddr_storage结构的定义(rfc2553)。根据rfc2553,sockaddr_storage应该与64位边界对齐,并且应该能够容纳sockaddr_in和sockaddr_in6。此外,它必须至少有一个__ss_family成员。其余字段是实现定义的。
#define _SS_MAXSIZE    128  /* Implementation specific max size */
#define _SS_ALIGNSIZE  (sizeof (int64_t))
                         /* Implementation specific desired alignment */
/*
 * Definitions used for sockaddr_storage structure paddings design.
 */
#define _SS_PAD1SIZE   (_SS_ALIGNSIZE - sizeof (sa_family_t))
#define _SS_PAD2SIZE   (_SS_MAXSIZE - (sizeof (sa_family_t)+
                              _SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
    sa_family_t  __ss_family;     /* address family */
    /* Following fields are implementation specific */
    char      __ss_pad1[_SS_PAD1SIZE];
              /* 6 byte pad, this is to make implementation
              /* specific pad up to alignment field that */
              /* follows explicit in the data structure */
    int64_t   __ss_align;     /* field to force desired structure */
               /* storage alignment */
    char      __ss_pad2[_SS_PAD2SIZE];
              /* 112 byte pad to achieve desired size, */
              /* _SS_MAXSIZE value minus size of ss_family */
              /* __ss_pad1, __ss_align fields is 112 */
};

我的问题是为什么sockaddr_storage被定义成上面那样?为什么不能定义成下面这样?
struct sockaddr_storage {
    sa_family_t  __ss_family;     /* address family */
    char __ss_pad[_SS_MAXSIZE  - sizeof(sa_family_t) ]; //will there be any alignment issue here?
};
2个回答

6
你提出的替代方案不会强制整个结构体按照8字节(64位)边界对齐,这是RFC2553中提到的要求。一般来说,结构体采取其成员所需的最严格对齐方式。由于sa_family_t可能是u16_t,只需要2字节对齐,而char数组最多只需要1字节对齐,因此你提出的替代方案只需要2字节对齐。(编译器很可能会给它至少4字节、甚至8字节的对齐方式,但不能确定。)
实际定义的风格是为了确保结构体中的每个字节都是某个命名字段的一部分,即编译器不在字段之间插入任何填充。这是必要的(有点),以便_SS_PAD2SIZE具有可以根据所有其他成员的大小计算出的值。
然而,我觉得这个定义相当复杂。我非常确定以下定义同样有效,并且更容易理解:
struct sockaddr_storage {
    union {
        sa_family_t u_family;
        uint64_t u_pad[_SS_MAXSIZE / sizeof(uint64_t)];
    } __ss_u;
#   define __ss_family __ss_u.u_family
};

在这里,联合体获得了其最严格对齐成员的对齐要求,然后传播到封闭的结构体中。请注意,在此版本中,我只有一个必需字段(虽然它被隐藏在联合体中),以及一个大小恰好符合整个结构体要求的填充数组。唯一稍微棘手的部分是__ss_family宏定义。可能这种宏技巧并不完全符合RFC中的要求,但几乎没有任何方式可以发现其中的差异。

@Dale,用户应该访问的唯一sockaddr_storage字段是__ss_family。因此,即使其余字段对齐到64位边界,这又有什么帮助呢?另外,我错了,它是sockaddr_in6而不是sockaddr_storage应该对齐到64位边界。 - chappar
1
如果一个结构体或联合体包含任何要求64位对齐的成员(例如uint64_t),则该结构体本身将放置在64位边界上。否则,就无法保证该成员保持对齐。当然,如果结构体本身是64位对齐的,则第一个字段(__ss_family)也将是64位对齐的。 - mark4o

0
从你们所说的来看,似乎是这样的:
a.
int64_t   __ss_align;

导致结构体对齐到 64 位边界。

b. __ss_pad1 确保在 __ss_align 之前的 ss_family 在 64 位边界上

c. __ss_pad2 确保所需的总大小。

我读过上述代码,位于http://www.opengroup.org/onlinepubs/000095399/basedefs/sys/socket.h.html#tag_13_61。从那里写的内容来看,_SS_MAXSIZE 的值似乎由实现者决定。

在 Dale 提到的代码中,除法运算符

uint64_t u_pad[_SS_MAXSIZE / sizeof(uint64_t)]

将导致浮点数,然后被截断,这会导致结构体比所需的大小(_SS_MAXSIZE)小。

:)


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