Глобальный союз для разных модулей

У меня есть код MCU (для AVR) с набором разных драйверов. Используемый драйвер выбирается во время запуска (в коде инициализации), и одновременно будет использоваться только один из драйверов. Поэтому я могу связать все сегменты ОЗУ в одно и то же место. Это необходимо, так как оперативная память очень ограничена, и всем драйверам требуется 2-3 КБ памяти для буферов и т. д.

В настоящее время у меня есть структуры typedef во всех заголовочных файлах:

драйвер1.ч:

typedef struct {
    // Driver-specific variables for internal use.
    // None of those and accessed from outside C-file
    int internal_variable1_for_driver1;
    int internal_variable2_for_driver1;
    ...
} st_driver1_t;

Другие драйверы имеют аналогичную структуру

В all_drivers.h:

#include "driver1.h"
#include "driver2.h"
#include "driver3.h"
...
union {
    st_driver1_t DRV1;
    st_driver2_t DRV2;
    st_driver3_t DRV3;
    ...
} DRV;

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я понимаю, что весь доступ к ОЗУ из модулей, кроме выбранного, должен быть отключен, так как это изменит весь союз. У меня в портфолио много кода ASM, и это очевидно для меня.

Теперь у меня есть некоторые конфликты, потому что я должен включить all_drivers.h и, следовательно, все файлы driverN.h в каждый драйвер. Я хочу скрыть все эти заголовки от всех других драйверов.

Можно ли определить все специфичные для драйвера структуры с помощью __attribute__((common)) в C-файлах и, следовательно, скрыть всю структуру из заголовков. Насколько я понимаю, компоновщик объединит все эти структуры в перекрывающийся сегмент и создаст некий аналог объединения.

драйвер1.с:

struct __attribute__((common)) {
    int var1;
    int var2;
    ...
} DRV;

драйвер2.с:

struct __attribute__((common)) {
    long OtherVar1;
    int ExtraOne;
    ...
} DRV;

Кроме того, должен ли я определять эти структуры как static или нет? Будет ли это решение стабильным или это недокументированный хак?

Вы не должны использовать ничего из этого, включая странный союз, который выглядит как неправильное решение для реализации либо переключателя компилятора, либо полиморфизма. Забудьте и о нестандартных вещах GNU. static обычно достаточно хорош в качестве частной инкапсуляции бедняка для простых драйверов. Если вам нужно что-то более сложное, например несколько экземпляров одного и того же драйвера, обратите внимание на непрозрачные типы: Как сделать приватную инкапсуляцию в C?

Lundin 17.01.2023 10:25

«все драйверы требуют 2-3 КБ памяти для буферов и т. д.» Это звучит как очень странный дизайн. Это драйверы ЖК-дисплея или зачем вам столько оперативной памяти?

Lundin 17.01.2023 10:26

@Lundin У меня есть универсальное устройство, которое должно работать с большим количеством ведомых устройств, хотя все они используют несовместимые протоколы. Оперативная память используется в основном для буферов Rx/Tx (около 1 КБ каждый). Рабочий режим выбирается с помощью переключателя времени выполнения (из EEPROM)

NevaDA 18.01.2023 09:22

Какая шина требует таких больших буферов? Ethernet с полноценным TCP/IP?

Lundin 18.01.2023 09:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Можно ли определить все специфичные для драйвера структуры с помощью __attribute__((common))

С GCC и совместимыми компиляторами вы можете использовать __attribute__((__common__)) для общих переменных (и __attribute__((__no_common__)), если некоторые переменные не должны быть общими, но по умолчанию используются общие).

GCC изменил поведение по умолчанию с -fcommon на -fno-common некоторое время назад (в 2020 году с v10), поэтому вы также можете скомпилировать все единицы компиляции, которые в этом нуждаются, с помощью -fcommon.

должен ли я определить эти структуры как static [...]?

Нет. Есть несколько условий, которым должны соответствовать объекты, чтобы они были объединены как общие:

  • Объекты являются глобальными (таким образом, они также находятся в статическом хранилище).

  • Объекты не имеют инициализаторов.

  • Объекты имеют одинаковое имя (сборка).

  • Компилятору следует сообщить, что объекты должны быть общими, как обсуждалось выше.

Иногда желательно, чтобы объекты не имели одинаковых имен на уровне C/C++, например, чтобы объект для driver1 назывался driver1 и т. д. Это нормально, если объекты имеют одинаковые имена на уровне сборки, чего можно добиться с помощью одно и то же имя сборки для всех таких объектов. Это функция, предоставляемая GCC. В следующем примере имя сборки задается как asm_name, поэтому в коде сборки будет использоваться имя asm_name, но в коде C вы используете driver1:

typedef struct { ... } driver1_t;

__attribute__((__common__))
driver1_t driver1 __asm ("asm_name");

Если ABI требует подчеркивания в начале, вы должны использовать имя, которое соответствует этому ABI, и использовать имя сборки, начинающееся с _, например _asm_name.

Есть случаи использования, когда common намного понятнее и удобнее, чем объединение всех объектов в объединение, так что не расстраивайтесь из-за этих комментариев.

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