Можно ли прервать процесс копирования структуры прерыванием во встроенном C?

Внутри драйвера у меня есть функция для копирования данных из внутренней структуры в структуру из приложения.

Может ли этот процесс быть прерван триггером прерывания микроконтроллера?

uint16_t getRawData(struct Data *Data_external)
{
  if(Data_external == NULL)
  {
    return ERR_PARA;
  }
  else
  {
    *Data_external = Data_internal;            // the copy process. Could this be interrupted? 
  }
  return ERR_NONE;
}
На самом деле не существует «процесса копирования», будут инструкции, копирующие байты, и если они не являются атомарными, их можно прервать, это зависит от вашей архитектуры, компилятора, настроек и выравнивания.
В C нет гарантии, что процесс копирования структуры не может быть прерван. Однако обычно предполагается, что прерывание возвращается к той точке кода, где процесс копирования может завершиться. Так что это, вероятно, не имеет значения, если только код прерывания не зависит от него по какой-то причине (что маловероятно в большинстве случаев). находится в процессе удаления или добавления «чанка» в буфер, когда происходит прерывание, которое также должно обрабатывать «чанки» в том же буфере.
@jonk: Пожалуйста, не отвечайте на вопрос в комментариях, так как это обходит обычный процесс проверки ответов, как обсуждается в мета
@DaveTweed Тогда вы и этот сайт потеряете мои комментарии. Я не пишу ответы, если у меня нет времени писать более полные. Это мой единственный способ работы, и я не собираюсь его менять. Если я почувствую, что у меня достаточно времени для «правильного ответа» по моему определению, я продолжу, как прежде. Но я очень усердно работаю над несколькими различными активными проектами, и у меня НЕТ времени на мой обычный уровень контекста и содержания ответов. Так что либо я пишу короткие комментарии как комментарии, либо сайт теряет доступ к моему времени. Ваш звонок. Мое время предоставляется на моих условиях или не предоставляется вовсе.
@jonk: Нет ничего плохого в том, чтобы написать краткий ответ, если он полный и автономный, как ваш комментарий. Ваша приверженность пространным ответам полностью зависит от вас. На сайте есть правила и процессы, и я просто пытаюсь подтолкнуть вас в правильном направлении. В конечном счете, это полностью зависит от вас - у вас могло быть 9 голосов (+90 повторений) за ваш ответ...
@DaveTweed Спасибо за толчок. Баллы в основном не имеют значения. Я думаю, что как только я добрался до «уменьшенной рекламы» в 200 баллов, я не заботился об остальном (и не использовал его). Я не думаю, что заслуживаю высоких цифр, которые у меня есть прямо сейчас. Я просто любитель, черт возьми. И я очень беспокоюсь, что люди могут ошибочно принять этот показатель репутации за то, что он значит для них больше, чем должен. Я бы предпочел, чтобы мои слова всегда оставались «подозрительными» и заслуживающими критики, а не воспринимались как некое евангелие. Если бы я мог сократить число повторений, я бы это сделал.
@jonk Если вы действительно хотите, чтобы люди могли «подозревать» ваши ответы, то, пожалуйста, позвольте людям правильно голосовать за них. В (маловероятном!) случае, если вы опубликуете что-то неправильное в качестве комментария, пользователи не смогут проголосовать против. Это может быть особенно проблемой, если люди голосуют за комментарий, который изначально кажется правильным (опять же, я не ставлю под сомнение качество ваших комментариев/ответов :))
@mbrig Вы можете меня неправильно понять. Я принимаю решение писать ответы как таковые и с прикрепленным моим именем только в том случае, если они включают полный контекст и содержание или в других случаях, когда я думаю, что могу предложить необычный или уникальный подход. Они также всегда исследуются в том смысле, что я не буду публиковать ответ, пока не перепроверю себя, найдя ссылки и вспомогательные материалы, которые одновременно согласуются с моей позицией, а также которые я могу процитировать, когда меня спросят. Я не просто пишу. Я пишу, затем исследую, перепроверяю и проверяю. Затем опубликуйте. В противном случае это не отображается как ответ от меня.
@mbrig Наконец, мой номер представителя не имеет смысла. То, что я пишу, может быть. Но это отдельная тема. Я просто хочу, чтобы я мог убрать или избавиться от своего номера представителя. Я рад, что некоторые люди нашли мой ответ (или два) полезными. Но у людей есть естественная тенденция воображать что-то другое, учитывая мое количество повторений, и я бы хотел, чтобы они этого не делали. Вот и все. Ваше предложение не решает ни одной из моих проблем.
@jonk: Вы можете начать вопрос о награде и наградить кого-то с репутацией 29k, чтобы избавиться от него;)
@Rev1.0 Я мог бы сделать это, если бы это также не требовало слишком много размышлений о создании проблемы / вопроса, достойного этого, и, возможно, также не смогло устранить проблему, на которую я указываю, а вместо этого просто передать ее дальше кому-то другому (что не касается моих опасений). Тем не менее, хороший творческий показ. ;)
@jonk: Хе-хе, тогда давайте сойдем с ума ;) 1. Создайте 2-ю учетную запись 2. Напишите классный вопрос + вознаграждение с 1-й учетной записи 3. Ответьте на вопрос, опубликовав отличный ответ, используя 2-ю учетную запись 4. Удалите 2-ю учетную запись -> Результат: Вы написали хороший вопрос, хороший ответ, и вы даже потеряли репутацию при этом .... все, что вы когда-либо хотели;)
@ Rev1.0 (1) У меня есть предчувствие, что это нарушит некоторую интерпретацию политики и приведет к тому, что меня забанят. (2) Я по-прежнему хотел бы, чтобы люди знали, кто я такой, а смена учетных записей довольно сильно усложняет или же лишает меня этой цели. (3) Кто-то (даже если это я) все еще набирает эти очки репутации. (4) Я люблю творческое мышление! Спасибо.

Ответы (7)

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

В особом случае обработчики прерываний могут сами прерываться прерываниями с более высоким приоритетом (вложенными прерываниями).

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

Помните, что в зависимости от архитектуры целевого процессора одна строка C может быть скомпилирована во множество ассемблерных инструкций. Простой i++на AVR скомпилирован в несколько инструкций, iнапример, uint32_t.

Что касается вашего последнего абзаца, я предполагаю, что это верно только для 16/8-битных систем, верно? Я знаю, вы говорите, что AVR... просто удостоверяюсь.
memcpy структуры (как в вопросе) также представляет собой несколько инструкций для 32-битной ARM. Инкрементация uint64_t на 32-битном ARM тоже не атомарна. Побитовые операции также представляют собой чтение-изменение-запись (если не используется битовая группировка).
@HarrySvensson нет - на любой архитектуре RMW, и большинство uC предназначены для чтения - изменения - записи (большинство процессоров RISC - это RMW). Таким образом, i++ будет генерировать во многих случаях (за исключением случаев, когда значение просто хранится в регистре, что здесь не так) три инструкции и на современных 32/64-битных ARM uC.
Ах, здесь произошло небольшое недоразумение. Я думал о том, чтобы позаботиться о переносе при выполнении инкрементов (переполнение), и вы оба говорите об извлечении из памяти и обратной записи. - Ну что ж.
В общем: все операции над типами, ширина которых превышает ширину шины данных, являются "фанковыми".

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

Вы можете подумать про себя: «На самом деле этого никогда не произойдет, сколько времени потребуется для копирования этого кода и насколько вероятно прерывание?» Но с большинством производственных встроенных приложений это произойдет, потому что продукт работает годами без обновлений.

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

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

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

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

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

Я просто собираюсь пойти дальше и предположить, что вы спросили об этом по очень серьезной причине.

*Data_external = Data_internal;

Может быть разделен (за исключением некоторых крайних случаев, которые вряд ли будут здесь задействованы).

Я не знаю ваш процессор, но я еще не видел процессор, который не может сделать моральный эквивалент:

cli(); /* mask all interrupts */
*Data_external = Data_internal;
sti(); /* restore interrupt mask */

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

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

Что такое cli и sti? из google они кажутся инструкциями для x86?
@Sam: Ну да, это названия инструкций в x86. Я думаю, что они имеют одинаковые имена в PDP-11, потому что некоторый код, который возник там, имел одинаковые имена функций для логического эквивалента в пользовательском режиме (маскировать межпроцессные сигналы). Вот инструкции для Arduino: tldp.org/LDP/tlk/dd/interrupts.html
Обычно вы отключаете прерывания, но разрешаете их только в том случае, если они действительно разрешены. В противном случае ваш код имеет побочный эффект, заключающийся в постоянном включении прерываний, даже если они были отключены до вызова кода.

Код, который вы представили, действительно может быть прерван. Однако, прежде чем вы начнете создавать критические разделы повсюду, вы должны проверить несколько вещей:

  • Вы говорите, что эта функция находится «внутри драйвера». Прерывания уже отключены при вызове этой функции? Или он вызывается внутри обработчика прерываний, который предотвращает срабатывание других прерываний? Если да, то операция фактически не может быть прервана.

  • Доступен ли Data_internalкогда-либо внутри обработчика прерывания? В противном случае нет никакого вреда, даже если операцию можно прервать.

[Недостаточно представителей для комментариев]

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

Неглубокая копия может, но вряд ли быть атомарной, в зависимости от архитектуры машины. Глубокая копия почти наверняка не является атомарной на любой архитектуре.

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

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

На типичном 32-битном микроконтроллере чтение и запись volatileцелочисленных типов с указанием -qualified, которые имеют размер 32 бита и меньше; назначение будет состоять из атомарного чтения, за которым следует атомарная запись. Если вы хотите, чтобы 32-разрядная структура копировалась атомарно, поместите ее в объединение с uint32_t, и прочитайте или запишите этот член, чтобы прочитать или записать структуру целиком. Качественные реализации, сконфигурированные так, чтобы они подходили для использования во встроенных системах, позволят использовать объединения таким образом, независимо от того, потребует ли Стандарт реализации, которые не предназначены для такого использования, делать то же самое. Обратите внимание, что gcc и clang не будут надежно работать как качественные реализации, подходящие для использования во встроенных системах, если не отключены различные оптимизации.