Система отправки сообщений на C, которая не нарушает строгое сглаживание и выравнивание

StackOverflow https://stackoverflow.com/questions/1290834

Вопрос

Я пишу встроенную систему управления на C, которая состоит из нескольких задач, которые отправляют сообщения друг другу (я полагаю, довольно распространенная идиома!), но мне трудно разработать механизм, который:

  • является аккуратным
  • является общим
  • является относительно эффективным
  • самое главное: не зависит от платформы (в частности, не нарушает строгое сглаживание или проблемы с выравниванием)

Концептуально я хотел бы представить каждый тип сообщения как отдельное определение структуры, и я хотел бы систему со следующими функциями (упрощенными):

void sendMsg(queue_t *pQueue, void *pMsg, size_t size);
void *dequeueMsg(queue_t *pQueue);

где a queue_t содержит связанный список узлов, каждый из которых имеет char buf[MAX_SIZE] поле.Система, в которой я нахожусь, не имеет malloc() реализация, поэтому потребуется создать глобальный пул свободных узлов, а затем одну из следующих (предполагаемые проблемы выделены жирным шрифтом):

  1. sendMsg() делает ли memcpy из входящего сообщения в буфер свободного узла.
    Насколько я понимаю, у этого будут проблемы с выравниванием, если вызывающий из dequeueMsg() делает дальнейший memcpy от возвращаемого значения.
  2. или там будет void *getFreeBuffer() функция, которая возвращает buf[] следующего свободного узла, который вызывающий (отправитель) преобразует в соответствующий указатель на тип.
    Насколько я понимаю, теперь у этого будут проблемы с выравниванием на входе, и все еще требуется memcpy после dequeueMsg() чтобы избежать проблем с выравниванием на выходе.
  3. или переопределите буфер в queue_t узлы как (например) uint32_t buf[MAX_SIZE].
    Насколько я понимаю, это нарушает строгое сглаживание и не зависит от платформы.

Единственный другой вариант, который я вижу, - это создать объединение всех типов сообщений вместе с char buf[MAX_SIZE], но я не считаю это "аккуратным"!

Итак, мой вопрос в том, как это сделать правильно?

Это было полезно?

Решение

Способ, которым мы справляемся с этим, заключается в том, чтобы наш бесплатный список состоял полностью из выровненных узлов.На самом деле у нас есть несколько бесплатных списков для разных размеров узла, поэтому у нас есть списки, выровненные по границам в 2 байта, 4 байта и 16 байт (наша платформа не заботится о выравнивании размером более одного SIMD-вектора).Любое распределение округляется до одного из этих значений и помещается в правильно выровненный узел.Таким образом, sendMsg всегда копирует свои данные в выровненный узел.Поскольку вы сами составляете бесплатный список, вы можете легко обеспечить выравнивание.

Мы также использовали бы #pragma или declspec, чтобы принудительно выровнять массив char buf[MAX_SIZE] по крайней мере по границе слова внутри структуры узла queue_t.

Это предполагает, конечно, что входные данные выровнены, но если по какой-то причине вы передаете сообщение, которое ожидает чтобы быть (скажем) отклоненным от выравнивания на 3 байта, вы всегда можете определить это с помощью модуля и вернуть смещение в свободный узел.

С этим базовым дизайном у нас есть интерфейсы, которые поддерживают оба варианта 1 и 2 выше.Опять же, мы предполагаем, что входные данные всегда изначально выровнены, поэтому наше удаление из очереди, конечно, возвращает выровненный указатель;но если вам нужны странно выровненные данные в, опять же, просто смещите в свободный узел и верните указатель смещения.

Это позволяет вам работать с void *, таким образом избегая ваших проблем со строгим наложением псевдонимов.(В целом, хотя я думаю, что вам, возможно, придется ослабить свои строгие требования к псевдонимам при написании собственных распределителей памяти, поскольку по своей природе они внутренне размывают типы.)

Другие советы

Я не понимаю, почему 1 представляет проблему выравнивания - до тех пор, пока каждый buf[MAX_SIZE] элемент выровнен по естественному самому большому одиночному примитивному типу, который встречается в ваших структурах сообщений (вероятно, 32 или 64 бит), тогда не имеет значения, каково содержимое каждого типа сообщения;поскольку он всегда будет выровнен по этому размеру.

Редактировать

На самом деле, все еще проще, чем это.Поскольку каждый элемент в вашей очереди сообщений является MAX_SIZE по длине, затем предполагая, что вы начинаете каждое сообщение с его собственного buf (т.е.вы не упаковываете их, если сообщение < MAX_SIZE) тогда каждое сообщение в очереди будет начинаться с границы, по крайней мере, такой же большой, как и оно само, поэтому оно всегда будет правильно выровнено.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top