выравнивание памяти в gcc структурах

Я портирую приложение на платформу ARM в C, приложение также работает на x86 процессоре и должно быть обратно совместимым.

У меня теперь есть некоторые проблемы с переменным выравниванием. Я прочитал gcc руководство для __attribute__((aligned(4),packed)) Я интерпретирую то, что говорится, поскольку запуск структуры выровненный к 4-байтовой границе, и внутренняя часть остается нетронутой из-за упакованного оператора.

первоначально у меня было это, но иногда это помещается невыровненное с 4-байтовой границей.

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((packed)) CHALLENGE;

таким образом, я изменяю его на это.

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((aligned(4),packed)) CHALLENGE;

Понимание, которое я заявил ранее, кажется, является неправильным, поскольку и структура теперь выровненная к 4-байтовой границе, и и внутренние данные теперь выровненные к четырехбайтовой границе, но из-за порядка байтов, размер структуры увеличился в размере с 42 до 44 байтов. Этот размер очень важен, поскольку у нас есть другие приложения, которые зависят от структуры, являющейся 42 байтами.

Могли некоторые описывать мне, как выполнить операцию, которой я требую. Любая справка очень ценится.

13
задан Ashe 30 March 2014 в 03:09
поделиться

5 ответов

Если вы полагаетесь на то, что sizeof (yourstruct) составляет 42 байта, вас вот-вот укусит мир не -портативные предположения. Вы не сказали, для чего это нужно, но похоже, что порядок байтов в содержимом структуры также имеет значение, так что у вас также может быть несоответствие с x86.

В этой ситуации я думаю, что единственный верный способ справиться - использовать unsigned char [42] в тех частях, где это важно. Начните с написания точной спецификации того, какие именно поля находятся в этом 42-байтовом блоке и какой порядок байтов, затем используйте это определение, чтобы написать некоторый код для перевода между этим и структурой, с которой вы можете взаимодействовать. Код, скорее всего, будет либо кодом сериализации «все сразу» (он же маршаллинг), либо кучей геттеров и сеттеров.

12
ответ дан 1 December 2019 в 23:31
поделиться

Какова ваша истинная цель?

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

Тогда все, с чем нужно разобраться и, возможно, иметь специфичный для платформы код, - это процедуры маршалинга. И вы можете написать несколько хороших и неприятных модульных тестов, чтобы гарантировать, что маршалированные данные попадают в структуру и из нее должным образом, независимо от того, на какую платформу вам, возможно, придется переносить сегодня и в будущем.

2
ответ дан 1 December 2019 в 23:31
поделиться

Это одна из причин, почему чтение целых структур, а не членов, не работает, и его следует избегать.

В данном случае упаковка плюс выравнивание по 4 означает, что будет два байта прокладки. Это происходит потому, что размер должен быть совместим для хранения типа в массиве со всеми элементами, выровненными по 4.

Я представляю, что у вас получилось что-то вроде:

read(fd, &obj, sizeof obj)

Поскольку вы не хотите читать эти 2 байта прокладки, которые принадлежат разным данным, вы должны явно указать размер:

read(fd, &obj, 42)

Что вы можете сделать для поддержания:

typedef struct {
  //...
  enum { read_size = 42 };
} __attribute__((aligned(4),packed)) CHALLENGE;

// ...

read(fd, &obj, obj.read_size)

Или, если вы не можете использовать некоторые особенности C++ в вашем C:

typedef struct {
  //...
} __attribute__((aligned(4),packed)) CHALLENGE;
enum { CHALLENGE_read_size = 42 };

// ...

read(fd, &obj, CHALLENGE_read_size)

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

5
ответ дан 1 December 2019 в 23:31
поделиться

Я предполагаю, что проблема в том, что 42 не делится на 4, и поэтому они выходят из выравнивания, если вы поместите несколько из этих структур друг за другом (например, выделите память для нескольких из них, определив размер с помощью размер ). В этих случаях размер 44 заставляет выравнивать, как вы и просили. Однако, если внутреннее смещение каждого члена структуры остается неизменным, вы можете рассматривать 44-байтовую структуру, как если бы она составляла 42 байта (если вы позаботитесь о выравнивании любых следующих данных по правильной границе).

Можно попробовать поместить обе этих структур в один тип объединения и использовать только 42-байтовую версию внутри каждого такого объединения.

0
ответ дан 1 December 2019 в 23:31
поделиться

Поскольку я использую linux, я обнаружил, что echo 3 > /proc/cpu/alignment выдает предупреждение и исправляет проблему выравнивания. Это обходной путь, но он очень помогает найти место, где структуры не выравниваются.

-2
ответ дан 1 December 2019 в 23:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: