Внутри драйвера у меня есть функция для копирования данных из внутренней структуры в структуру из приложения.
Может ли этот процесс быть прерван триггером прерывания микроконтроллера?
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;
}
Да. Практически все в MCU может быть прервано запросом на прерывание. Когда обработчик прерывания завершится, предыдущий код просто продолжит работу, поэтому обычно это не проблема.
В особом случае обработчики прерываний могут сами прерываться прерываниями с более высоким приоритетом (вложенными прерываниями).
Если последовательность инструкций не должна прерываться, вам необходимо реализовать критическую секцию (в основном глобально отключить прерывания, выполнить задание, снова включить).
Помните, что в зависимости от архитектуры целевого процессора одна строка C может быть скомпилирована во множество ассемблерных инструкций. Простой i++
на AVR скомпилирован в несколько инструкций, i
например, uint32_t
.
Прерывание может помешать любой операции, которая не является атомарной . Этот вид программирования часто очень отличается от большинства других программ и может сбивать с толку людей, которые не изучали конструкцию процессора или архитектуру компьютера.
Вы можете подумать про себя: «На самом деле этого никогда не произойдет, сколько времени потребуется для копирования этого кода и насколько вероятно прерывание?» Но с большинством производственных встроенных приложений это произойдет, потому что продукт работает годами без обновлений.
Другая проблема с подобными копиями структур заключается в том, что когда они действительно возникают, их чрезвычайно сложно отлаживать, потому что они происходят только тогда, когда прерывание происходит в нужное время (что может составлять всего один цикл).
Весь смысл прерываний в том, что они могут происходить (и происходят) все время и предназначены для нулевого влияния на любой код, который выполняется в момент их возникновения. Все регистры сохраняются, и в зависимости от архитектуры ЦП может быть заменен совершенно другой набор регистров, прерывание делает свое дело, а затем исходные регистры восстанавливаются, и код продолжает работать в обычном режиме.
Проблемы могут возникнуть, когда сама подпрограмма обслуживания прерывания пытается получить доступ к памяти, к которой обращается работающий прерванный код. Еще более незаметные ошибки могут возникать при прерывании критического по времени процесса ввода-вывода. Эти проблемы характерны для более старых, простых и менее безопасных архитектур, где может быть небольшое разделение между кодом режима «пользователь» и «супервизор/ядро».
Такого рода проблемы может быть трудно идентифицировать и часто трудно воспроизвести, но однажды выявленные, их часто довольно просто исправить с помощью защитного программирования, мьютексов/семафоров или просто путем отключения прерываний в критических участках кода.
Общий класс проблем был тщательно изучен, и современные многоядерные процессоры и даже многозадачные операционные системы были бы невозможны, если бы несколько решений еще не были опробованы и протестированы.
Я просто собираюсь пойти дальше и предположить, что вы спросили об этом по очень серьезной причине.
*Data_external = Data_internal;
Может быть разделен (за исключением некоторых крайних случаев, которые вряд ли будут здесь задействованы).
Я не знаю ваш процессор, но я еще не видел процессор, который не может сделать моральный эквивалент:
cli(); /* mask all interrupts */
*Data_external = Data_internal;
sti(); /* restore interrupt mask */
Теперь его нельзя разделить на какой-либо одноядерный процессор, потому что ничто не может прерывать, пока прерывания отключены. Хорошая это идея или нет, зависит от многих вещей, оценить которые я просто не имею права.
Если вы многоядерный (а я наконец вспомнил, что на рынке есть многоядерный встроенный процессор), не делайте этого. Это бесполезно. Вам нужно будет разработать правильную блокировку.
Код, который вы представили, действительно может быть прерван. Однако, прежде чем вы начнете создавать критические разделы повсюду, вы должны проверить несколько вещей:
Вы говорите, что эта функция находится «внутри драйвера». Прерывания уже отключены при вызове этой функции? Или он вызывается внутри обработчика прерываний, который предотвращает срабатывание других прерываний? Если да, то операция фактически не может быть прервана.
Доступен ли Data_internal
когда-либо внутри обработчика прерывания? В противном случае нет никакого вреда, даже если операцию можно прервать.
[Недостаточно представителей для комментариев]
Еще одна проблема с таким копированием структуры заключается в том, что это неглубокая копия . Вместо этого вам может понадобиться глубокая копия.
Неглубокая копия может, но вряд ли быть атомарной, в зависимости от архитектуры машины. Глубокая копия почти наверняка не является атомарной на любой архитектуре.
Качественная реализация, подходящая для использования во встраиваемых системах, документирует, как будут volatile
выполняться квалифицированные операции чтения или записи различных типов, достаточно подробно, чтобы указать, могут ли они быть «разделены» прерываниями и каким образом, а также разрешены ли операции чтения и записи энергозависимой памяти. упорядочены в отношении неквалифицированных операций чтения и записи. Хотя некоторые реализации могут вести себя так, как будто все операции чтения и записи являются volatile
квалифицированными, обычно предполагается, что реализации могут свободно обрабатывать последовательности неквалифицированных операций чтения и записи любым способом, наиболее эффективным при отсутствии промежуточных volatile
доступов.
На типичном 32-битном микроконтроллере чтение и запись volatile
целочисленных типов с указанием -qualified, которые имеют размер 32 бита и меньше; назначение будет состоять из атомарного чтения, за которым следует атомарная запись. Если вы хотите, чтобы 32-разрядная структура копировалась атомарно, поместите ее в объединение с uint32_t
, и прочитайте или запишите этот член, чтобы прочитать или записать структуру целиком. Качественные реализации, сконфигурированные так, чтобы они подходили для использования во встроенных системах, позволят использовать объединения таким образом, независимо от того, потребует ли Стандарт реализации, которые не предназначены для такого использования, делать то же самое. Обратите внимание, что gcc и clang не будут надежно работать как качественные реализации, подходящие для использования во встроенных системах, если не отключены различные оптимизации.
ПлазмаHH
придурок
Дэйв Твид
придурок
Дэйв Твид
придурок
мбриг
придурок
придурок
Преподобный
придурок
Преподобный
придурок