Как я могу быть уверен, что эта структура C упакована как в 32-битных, так и в 64-битных системах? Всегда ли необходим __attribute__((упакованный))?

Я пытаюсь создать простой пользовательский протокол прикладного уровня, содержащий метку времени и некоторую другую полезную информацию, чтобы выполнить несколько измерений сети между различными системами Linux.

Реализация, по моей первоначальной идее, должна быть максимально переносимой между различными платформами (x86, ARM, ...), насколько это касается систем Linux.

Чтобы управлять заголовком, я создал эту структуру:

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? Или в любом случае будет добавлен отступ для выравнивания каждого поля с последним, которое является самым большим?

Что происходит тогда в 64-битных системах, где последнее поле составляет 16 байтов? Я думаю, что структура больше не будет "упакована по макету", в любом случае (это правильно?).

Добавляет __attribute__((packed)) Достаточно и всегда необходимо, чтобы гарантировать, что структура упакована, когда код компилируется для разных платформ? Есть ли лучшие решения?

1 ответ

Решение

Если вы определяете проводной протокол, вам действительно нужно использовать свой собственный тип. Чтобы быть в безопасности, вы должны использовать 64 бита для секунд с 1970 года, даже если многие 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) будет равно sizeof (struct timeval)это все еще уменьшает выравнивание всей структуры, включая sendtime член, до 1, поэтому член может быть смещен и непригоден для прямого использования с функциями, которые ожидали struct timeval *,

Другие вопросы по тегам