Синхронизация данных без использования семафора в C

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

Любая помощь будет оценена

3 ответа

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

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

Так что вам нужен совершенно другой механизм!

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

Как? Ну, в зависимости от ОС / оборудования - пока вы не предоставите дополнительную информацию, я здесь...

Еще несколько советов: сделайте период отключенных прерываний как можно короче и убедитесь, что общедоступные данные объявлены как изменчивые!

Что вам нужно, так это атомарный доступ к данным. Если это одна переменная, и вы можете гарантировать, что доступ является атомарным, то этого достаточно. Однако это включает в себя дизассемблирование кода C и просмотр результатов. И даже если машинный код окажется атомарным (одиночная инструкция), он не будет переносимым.

Если у вас есть современный компилятор с поддержкой C11, вы можете объявить переменную общего доступа как _Atomic и это решит проблему.

Другой альтернативой является простое отключение определенного прерывания во время доступа к переменной в вызывающей стороне. Это, однако, нарушит производительность в реальном времени, и вы можете пропустить прерывания.

Универсально "лучшим" решением может быть создание собственного семафора. Пример:

// volatile to prevent dangerous compiler optimizations; does not solve re-entrancy
volatile uint32_t data;
volatile bool guard;

void ISR (void)
{
  if(!guard)
  {
    data = SOME_REGISTER;
  }
}

void main (void)
{
  ...
  guard = true;
  uint32_t local = data;
  guard = false;
}

В приведенном выше примере никакой атомарный доступ не гарантируется вообще, даже к guard переменная. Тем не менее, это больше не нужно, потому что в точке, где main() собирается прочитать данные, guard гарантированно будет установлен. Если прерывание сработает во время чтения, оно не повредит данные.

Единственным недостатком этого решения является то, что вы пропустите обновления data когда охранник установлен. Если это проблема, вам придется внедрить некоторый способ временного хранения.

(Обратите внимание, что этот код не приводит к "барьерам памяти", поэтому на сложных многоядерных процессорах этот метод может не работать и volatile не обязательно приведет к барьеру памяти. На обычных микроконтроллерах все будет работать нормально.)

Это, вероятно, так же просто, как это в вашем основном коде:

disable_interrupts();
value += 1;
enable_interrupts();

Таким образом, вы убедитесь, что прерывание не может сработать, пока вы используете значение в основном коде.

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