Мы компилируем встроенное приложение C / C ++, которое развертывается в экранированном устройстве в среде, засыпанной ионизирующим излучением . Мы используем GCC и кросс-компиляцию для ARM. При развертывании наше приложение генерирует некоторые ошибочные данные и вылетает чаще, чем хотелось бы. Аппаратное обеспечение разработано для этой среды, и наше приложение работает на этой платформе в течение нескольких лет.
Есть ли какие-либо изменения, которые мы можем внести в наш код, или улучшения во время компиляции, которые могут быть сделаны для выявления / исправления программных ошибок и повреждения памяти, вызванного сбоями одного события ? Удалось ли другим разработчикам снизить вредное воздействие программных ошибок на долго работающее приложение?
Ответы:
Работая около 4-5 лет над разработкой программного обеспечения / прошивки и тестированием среды миниатюрных спутников *, я хотел бы поделиться своим опытом здесь.
* ( миниатюрные спутники намного более склонны к одиночным событиям, чем большие спутники из-за его относительно небольших, ограниченных размеров для его электронных компонентов )
Теперь эта ситуация обычно обрабатывается как на аппаратном, так и на программном уровне. Здесь, по вашей просьбе, я поделюсь тем, что мы можем сделать на уровне программного обеспечения.
... восстановление ... цель . Предоставьте возможность обновлять / перекомпилировать / перепрошивать ваше программное обеспечение / прошивку в реальных условиях. Это почти обязательная функция для любого программного обеспечения / прошивки в сильно ионизированной среде. Без этого вы могли бы иметь избыточное программное / аппаратное обеспечение столько, сколько захотите, но в один прекрасный момент все они взорвутся. Итак, подготовьте эту функцию!
... минимальная рабочая версия ... Имейте отзывчивые, множественные копии, минимальную версию программного обеспечения / прошивки в вашем коде. Это похоже на безопасный режим в Windows. Вместо того, чтобы иметь только одну полнофункциональную версию вашего программного обеспечения, имейте несколько копий минимальной версии вашего программного обеспечения / прошивки. Минимальная копия обычно имеет гораздо меньший размер, чем полная копия, и почти всегда имеет только следующие две или три функции:
... скопировать ... куда-нибудь ... иметь избыточное программное обеспечение / прошивку где-нибудь.
Вы можете, с избыточным аппаратным обеспечением или без него, попытаться установить избыточное программное обеспечение / прошивку в вашем ARM uC. Обычно это делается с помощью двух или более идентичных программ / микропрограмм по разным адресам, которые посылают друг другу пульс, но одновременно будет активен только один. Если известно, что одно или несколько программ / микропрограмм не отвечают, переключитесь на другое программное обеспечение / микропрограммное обеспечение. Преимущество использования этого подхода заключается в том, что мы можем сразу заменить функциональную замену после возникновения ошибки - без какого-либо контакта с какой-либо внешней системой / стороной, ответственной за обнаружение и устранение ошибки (в случае спутника обычно это Центр управления полетом ( MCC)).
Строго говоря, недостаток аппаратного обеспечения заключается в том, что вы фактически не можете устранить все отдельные точки сбоев. По крайней мере, у вас все еще будет одна единственная точка отказа, а именно сам коммутатор (или часто начало кода). Тем не менее, для устройства, ограниченного по размеру в сильно ионизованной среде (например, пико / фемтосателлиты), уменьшение единой точки отказа до одной точки без дополнительного оборудования все же стоит рассмотреть. Более того, кусок кода для переключения, безусловно, будет намного меньше, чем код для всей программы, что значительно снижает риск попадания в него одного события.
Но если вы этого не делаете, у вас должна быть хотя бы одна копия во внешней системе, которая может войти в контакт с устройством и обновить программное обеспечение / прошивку (в случае спутника это снова центр управления полетами).
... обнаруживаемая ошибочная ситуация. Ошибка должна быть обнаружена , обычно с помощью аппаратной схемы исправления ошибок / обнаружения или небольшого фрагмента кода для исправления ошибок / обнаружения. Лучше всего ставить такой код маленьким, множественным и независимым от основного программного обеспечения / прошивки. Его основная задача только для проверки / исправления. Если аппаратная схема / прошивка надежна(например, он более защищен от радиации, чем остальные - или имеет несколько схем / логических схем), тогда вы можете рассмотреть возможность исправления ошибок с его помощью. Но если это не так, лучше сделать это как обнаружение ошибок. Коррекция может осуществляться внешней системой / устройством. Для исправления ошибок вы могли бы рассмотреть возможность использования базового алгоритма исправления ошибок, такого как Hamming / Golay23, потому что они могут быть легче реализованы как в схеме / программном обеспечении. Но в конечном итоге это зависит от возможностей вашей команды. Для обнаружения ошибок обычно используется CRC.
... аппаратное обеспечение, поддерживающее восстановление. Теперь самое сложное в этом вопросе. В конечном счете, восстановление требует, чтобы оборудование, которое отвечает за восстановление, было как минимум функциональным. Если оборудование постоянно повреждено (обычно это происходит после того, как его общая ионизирующая доза достигает определенного уровня), то (к сожалению) у программного обеспечения нет возможности помочь в восстановлении. Таким образом, аппаратное обеспечение по праву является первостепенной задачей для устройства, подверженного высокому уровню излучения (такого как спутник).
В дополнение к предложению о вышеупомянутом ожидании ошибки прошивки из-за одного события, я также хотел бы предложить вам:
Алгоритм обнаружения и / или исправления ошибок в межсистемном протоколе связи. Это еще одно, почти необходимо иметь, чтобы избежать неполных / неправильных сигналов, полученных от другой системы
Фильтр в вашем чтении АЦП. Как не использовать АЦП для чтения непосредственно. Фильтруйте его по медианному фильтру, среднему фильтру или любым другим фильтрам - никогда не доверяйте единственному значению чтения. Образец больше, а не меньше - разумно.
источник
У НАСА есть документ о радиационно-стойком программном обеспечении. Он описывает три основные задачи:
Обратите внимание, что частота сканирования памяти должна быть достаточно частой, чтобы редко возникали многобитовые ошибки, поскольку большая часть памяти ECC может восстанавливаться после однобитовых, а не многобитовых ошибок.
Надежное восстановление после ошибок включает передачу потока управления (как правило, перезапуск процесса в точке до возникновения ошибки), освобождение ресурса и восстановление данных.
Их основная рекомендация по восстановлению данных состоит в том, чтобы избежать необходимости в этом, поскольку промежуточные данные рассматриваются как временные, так что перезапуск до того, как ошибка также откатит данные до надежного состояния. Это звучит аналогично понятию «транзакции» в базах данных.
Они обсуждают методы, особенно подходящие для объектно-ориентированных языков, таких как C ++. Например
И так уж вышло, НАСА использовало C ++ для крупных проектов, таких как Mars Rover .
Они избегали определенных функций C ++, которые могут создавать проблемы:
new
иdelete
)new
чтобы избежать возможности повреждения кучи системы).источник
Вот некоторые мысли и идеи:
Используйте ROM более творчески.
Храните все, что вы можете в ROM. Вместо того, чтобы вычислять вещи, храните таблицы поиска в ПЗУ. (Убедитесь, что ваш компилятор выводит таблицы поиска в раздел только для чтения! Распечатайте адреса памяти во время выполнения, чтобы проверить!) Сохраните таблицу векторов прерываний в ПЗУ. Конечно, запустите несколько тестов, чтобы увидеть, насколько надежна ваша ПЗУ по сравнению с вашей ОЗУ.
Используйте вашу лучшую оперативную память для стека.
SEU в стеке, вероятно, являются наиболее вероятным источником сбоев, потому что именно там обычно живут такие вещи, как индексные переменные, переменные состояния, адреса возврата и указатели различных типов.
Реализуйте процедуры таймера-тика и сторожевого таймера.
Вы можете запускать процедуру проверки работоспособности каждый тик таймера, а также подпрограмму сторожевого таймера для обработки блокировки системы. Ваш основной код также может периодически увеличивать счетчик, чтобы указывать прогресс, и процедура проверки работоспособности может гарантировать, что это произошло.
Реализуйте коды исправления ошибок в программном обеспечении.
Вы можете добавить избыточность к своим данным, чтобы иметь возможность обнаруживать и / или исправлять ошибки. Это добавит время обработки, потенциально оставляя процессор подверженным облучению в течение более длительного времени, увеличивая тем самым вероятность ошибок, поэтому вы должны учитывать компромисс.
Помните тайники.
Проверьте размеры кэшей вашего процессора. Данные, к которым вы недавно обращались или изменяли, вероятно, будут в кеше. Я полагаю, что вы можете отключить по крайней мере некоторые из кэшей (с большой производительностью); Вы должны попробовать это, чтобы увидеть, насколько кеши восприимчивы к SEU. Если кеши более устойчивы, чем ОЗУ, то вы можете регулярно читать и перезаписывать критически важные данные, чтобы убедиться, что они остаются в кеше и вернуть ОЗУ в рабочее состояние.
Умно используйте обработчики ошибок страниц.
Если вы отметите страницу памяти как отсутствующую, ЦП выдаст ошибку страницы при попытке доступа к ней. Вы можете создать обработчик ошибок страницы, который выполняет некоторую проверку перед обслуживанием запроса на чтение. (Операционные системы ПК используют это для прозрачной загрузки страниц, которые были выгружены на диск.)
Используйте язык ассемблера для критических вещей (которые могут быть всем).
С языком ассемблера вы знаете, что находится в регистрах, а что в оперативной памяти; Вы знаете, какие специальные таблицы ОЗУ использует ЦП, и можете разрабатывать обходные пути, чтобы снизить риск.
Используйте,
objdump
чтобы реально посмотреть на сгенерированный ассемблер и выяснить, сколько кода занимает каждая ваша подпрограмма.Если вы используете большую ОС, такую как Linux, вы напрашиваетесь на неприятности; просто так много сложности и так много вещей, которые могут пойти не так.
Помните, что это игра вероятностей.
Комментатор сказал
Хотя это действительно так, вероятность ошибок в (скажем) 100 байтах кода и данных, необходимых для правильной работы процедуры проверки, намного меньше, чем вероятность ошибок в других местах. Если ваш ПЗУ достаточно надежен и почти весь код / данные фактически находятся в ПЗУ, ваши шансы еще выше.
Используйте избыточное оборудование.
Используйте 2 или более идентичных аппаратных настроек с идентичным кодом. Если результаты отличаются, необходимо выполнить сброс. С 3 или более устройствами вы можете использовать систему «голосования», чтобы попытаться определить, какое из них было взломано.
источник
Вас также может заинтересовать обширная литература по теме алгоритмической отказоустойчивости. Это включает в себя старое назначение: написать сортировку, которая правильно сортирует свои входные данные, когда постоянное число сравнений не удастся (или, что более опасная версия, когда асимптотическое число неудачных сравнений масштабируется как
log(n)
дляn
сравнений).Место, где можно начать читать, - статья Хуанга и Авраама 1984 года " Отказоустойчивость на основе алгоритмов для матричных операций ». Их идея в некоторой степени похожа на гомоморфные зашифрованные вычисления (но на самом деле это не то же самое, поскольку они пытаются обнаружить / исправить ошибки на уровне операций).
Более поздний потомок этого документа - Босилька, Дельмас, Донгарра и Лангу, «Отказоустойчивость на основе алгоритмов, применяемая для высокопроизводительных вычислений ».
источник
Написание кода для радиоактивных сред на самом деле ничем не отличается от написания кода для любого критически важного приложения.
В дополнение к тому, что уже упоминалось, вот несколько разных советов:
ВАЖНО: Вы должны обеспечить целостность внутренних регистров MCU. Все регистры управления и состояния аппаратных периферийных устройств, которые доступны для записи, могут находиться в оперативной памяти и поэтому уязвимы.
Чтобы защитить себя от повреждений реестра, желательно выбрать микроконтроллер со встроенными функциями «однократной записи» регистров. Кроме того, вам нужно хранить значения по умолчанию всех аппаратных регистров в NVM и регулярно копировать эти значения в свои регистры. Вы можете обеспечить целостность важных переменных таким же образом.
Примечание: всегда используйте защитное программирование. Это означает, что вы должны настроить все регистры в MCU, а не только те, которые используются приложением. Вы не хотите, чтобы какое-то аппаратное периферийное устройство внезапно просыпалось.
Существуют всевозможные методы проверки ошибок в ОЗУ или NVM: контрольные суммы, «шаблоны ходьбы», программный ECC и т. Д. И т. Д. На сегодняшний день лучшим решением является не использование какого-либо из них, а использование MCU со встроенным ECC и аналогичные проверки. Поскольку делать это в программном обеспечении сложно, и проверка ошибок сама по себе может привести к ошибкам и неожиданным проблемам.
Понять и принять концепцию защитного программирования. Это означает, что ваша программа должна обрабатывать все возможные случаи, даже те, которые не могут возникнуть в теории. Примеры .
Высококачественная критически важная прошивка обнаруживает как можно больше ошибок, а затем безопасно их игнорирует.
ВАЖНО: не используйте значения по умолчанию для статических переменных продолжительности хранения. То есть не доверяйте содержимому по умолчанию
.data
или.bss
. Между моментом инициализации и моментом, когда переменная фактически используется, может быть сколько угодно времени, могло быть достаточно времени для повреждения ОЗУ. Вместо этого напишите программу так, чтобы все такие переменные устанавливались из NVM во время выполнения, незадолго до того времени, когда такая переменная используется в первый раз.На практике это означает, что если переменная объявлена в области видимости файла или как
static
, вы никогда не должны использовать ее=
для инициализации (или вы могли бы, но это бессмысленно, потому что вы все равно не можете полагаться на значение). Всегда устанавливайте его во время выполнения, перед использованием. Если есть возможность многократно обновлять такие переменные из NVM, то сделайте это.Точно так же в C ++ не полагайтесь на конструкторы для статических переменных продолжительности хранения. Пусть конструктор (ы) вызовет общедоступную подпрограмму «set-up», которую вы также можете вызвать позже во время выполнения, прямо из приложения вызывающей стороны.
Если возможно, удалите код запуска «copy-down», который полностью инициализирует
.data
и.bss
(и вызывает конструкторы C ++), чтобы вы могли получить ошибки компоновщика, если вы пишете код, полагаясь на него. Многие компиляторы имеют возможность пропустить это, обычно называемое «минимальный / быстрый запуск» или подобное.Это означает, что любые внешние библиотеки должны быть проверены, чтобы они не содержали такой зависимости.
Реализуйте и определите безопасное состояние для программы, куда вы будете возвращаться в случае критических ошибок.
источник
TRUE
равным, а0xffffffff
затем использоватьPOPCNT
с порогом.%01010101010101010101010101010101
, XOR, тогда, POPCNT?.text
разделе, изменив код операции или подобный.Может быть возможно использовать C для написания программ, которые ведут себя надежно в таких средах, но только если большинство форм оптимизации компилятора отключены. Оптимизирующие компиляторы предназначены для замены многих, казалось бы, избыточных шаблонов кодирования на «более эффективные», и могут не иметь ни малейшего представления о том, что причина, по которой программист тестирует,
x==42
когда компилятор знает, что нет другого способаx
удержать что-либо еще, заключается в том, что программист хочет предотвратить выполнение определенного кода с сохранениемx
некоторого другого значения - даже в тех случаях, когда единственным способом удержать это значение было бы, если бы система получала какой-то электрический сбой.Объявление переменных, как
volatile
это часто бывает полезно, но не может быть панацеей. Особо важно отметить, что безопасное кодирование часто требует, чтобы опасные операции имели аппаратные блокировки, для активации которых требуется несколько шагов, и чтобы код был написан с использованием шаблона:Если компилятор переводит код относительно буквально, и если после проверки повторяются все проверки состояния системы
prepare_for_activation()
, система может быть устойчивой практически к любому правдоподобному событию с одним единственным затруднением, даже к тем, которые могут произвольно повредить счетчик программ и стек. Если сбой происходит сразу после вызоваprepare_for_activation()
, это будет означать, что активация была бы уместной (так какprepare_for_activation()
до сбоя не было вызвано никакой другой причины ). Если сбой приводит кprepare_for_activation()
неправильному доступу к коду , но при этом нет последующих событий сбоя, у кода не будет возможности достичь его,trigger_activation()
не пройдя проверку проверки или сначала не вызвав cancel_preparations [если стек выпадает, выполнение может продолжаться как раз передtrigger_activation()
после того, как контекст, который вызвал,prepare_for_activation()
возвращается, но вызов кcancel_preparations()
произошел бы между вызовами к , таким образом делая последний вызов безопасным.prepare_for_activation()
иtrigger_activation()
Такой код может быть безопасным в традиционном Си, но не с современными компиляторами Си. Такие компиляторы могут быть очень опасными в такой среде, потому что агрессивно они стремятся включать только код, который будет уместен в ситуациях, которые могут возникнуть через какой-то четко определенный механизм, и чьи последствия будут также хорошо определены. Код, целью которого будет обнаружение и очистка после сбоев, в некоторых случаях может ухудшить ситуацию. Если компилятор определяет, что попытка восстановления в некоторых случаях вызовет неопределенное поведение, это может означать, что условия, которые могут потребовать такого восстановления в таких случаях, не могут возникнуть, таким образом устраняя код, который бы проверял их.
источник
-O0
или эквивалентный переключатель? GCC будет делать много странных вещей, если вы дадите ему разрешение , но если вы попросите его не делать этого, он, как правило, тоже может быть довольно буквальным.-O2
.-O0
плохой идеи заключается в том, что она издает гораздо более бесполезные инструкции. Пример: не встроенный вызов содержит инструкции по сохранению регистров, выполнению вызова, восстановлению регистров. Все это может потерпеть неудачу. Инструкция, которой нет, не может потерпеть неудачу.-O0
это плохая идея: она склонна хранить переменные в памяти, а не в регистре. Теперь нет уверенности, что память более восприимчива к SEU, но данные в полете более восприимчивы, чем данные в покое. Бесполезного перемещения данных следует избегать, и это-O2
помогает.v1=v2+0xCAFEBABE
и все обновления двух переменных сделаны ...Это чрезвычайно широкий предмет. По сути, вы не можете действительно восстановиться после повреждения памяти, но вы можете, по крайней мере, попытаться быстро потерпеть неудачу . Вот несколько методов, которые вы могли бы использовать:
постоянные данные контрольной суммы . Если у вас есть какие-либо данные конфигурации, которые остаются постоянными в течение длительного времени (включая настроенные вами аппаратные регистры), вычислите его контрольную сумму при инициализации и проверяйте ее периодически. Когда вы видите несоответствие, пришло время для повторной инициализации или сброса.
хранить переменные с избыточностью . Если у вас есть важная переменная
x
, написать свое значение вx1
,x2
иx3
и читать его как(x1 == x2) ? x2 : x3
.реализовать мониторинг потока программ . XOR - глобальный флаг с уникальным значением в важных функциях / ветвях, вызываемых из основного цикла. Запуск программы в среде без излучения с почти 100% тестовым покрытием должен дать вам список допустимых значений флага в конце цикла. Сброс, если вы видите отклонения.
контролировать указатель стека . В начале основного цикла сравните указатель стека с его ожидаемым значением. Сброс на отклонение.
источник
Что может вам помочь, так это сторожевой пес . В 1980-х годах сторожевые устройства широко использовались в промышленных вычислениях. Аппаратные сбои были гораздо более распространенными, чем другой - другой ответ также относится к этому периоду.
Сторожевой таймер - это комбинированная аппаратная / программная функция. Аппаратное обеспечение представляет собой простой счетчик, который отсчитывает от числа (скажем, 1023) до нуля. TTL или другая логика может быть использована.
Программное обеспечение было разработано таким образом, чтобы одна программа контролировала правильную работу всех основных систем. Если эта процедура завершается правильно = обнаруживает, что компьютер работает нормально, для счетчика возвращается значение 1023.
Общая конструкция такова, что в нормальных условиях программное обеспечение предотвращает достижение аппаратным счетчиком нуля. Если счетчик достигает нуля, аппаратная часть счетчика выполняет свою единственную задачу и сбрасывает всю систему. С точки зрения счетчика ноль равен 1024, и счетчик продолжает обратный отсчет.
Этот сторожевой таймер обеспечивает перезагрузку подключенного компьютера во многих случаях сбоя. Должен признать, что я не знаком с оборудованием, способным выполнять такую функцию на современных компьютерах. Интерфейсы к внешнему оборудованию теперь намного сложнее, чем раньше.
Недостатком сторожевого таймера является то, что система недоступна с момента сбоя, пока счетчик сторожевого таймера не достигнет нуля + время перезагрузки. Хотя это время обычно намного короче, чем любое внешнее или человеческое вмешательство, поддерживаемое оборудование должно быть в состоянии работать без компьютерного контроля в течение этого периода.
источник
В этом ответе предполагается, что вы заинтересованы в том, чтобы система работала корректно, помимо системы с минимальной стоимостью или высокой скоростью; большинство людей, играющих с радиоактивными вещами, ценят правильность / безопасность, а не скорость / стоимость.
Несколько человек предложили внести изменения в аппаратные средства, которые вы можете внести (хорошо - здесь уже есть много полезного в ответах, и я не собираюсь повторять все это), а другие предложили избыточность (в принципе здорово), но я не думаю, что кто-нибудь предположил, как эта избыточность может работать на практике. Как вы терпите неудачу? Как вы знаете, когда что-то «пошло не так»? Многие технологии работают на основе того, что все будет работать, и поэтому неудача - это сложная задача. Тем не менее, некоторые распределенные вычислительные технологии , предназначенные для масштабирования ожидать сбоя (в конце концов, при достаточном масштабе сбой одного узла из множества неизбежен при любой MTBF для одного узла); Вы можете использовать это для своего окружения.
Вот несколько идей:
Убедитесь, что все ваше оборудование реплицировано
n
раз (гдеn
больше 2, и предпочтительно нечетное), и что каждый аппаратный элемент может связываться друг с другом аппаратным элементом. Ethernet является одним из очевидных способов сделать это, но есть много других гораздо более простых маршрутов, которые обеспечат лучшую защиту (например, CAN). Минимизируйте общие компоненты (даже источники питания). Это может означать, например, выборку входов АЦП в нескольких местах.Убедитесь, что состояние вашего приложения находится в одном месте, например, в конечном автомате. Это может быть полностью основано на оперативной памяти, хотя не исключает стабильного хранения. Таким образом, он будет храниться в нескольких местах.
Принять протокол кворума для изменений состояния. Смотрите РАФТ для примера. Поскольку вы работаете в C ++, для этого есть хорошо известные библиотеки. Изменения в FSM будут сделаны только тогда, когда большинство узлов согласится. Используйте известную хорошую библиотеку для стека протоколов и протокола кворума вместо того, чтобы самим ее свернуть, иначе вся ваша хорошая работа по резервированию будет потрачена впустую, когда протокол кворума зависнет.
Убедитесь, что вы проверяли контрольную сумму (например, CRC / SHA) своего FSM и сохраняли CRC / SHA в самом FSM (а также передавали в сообщении и проверяли контрольные суммы самих сообщений). Получите, чтобы узлы регулярно проверяли свой FSM на соответствие этой контрольной сумме, проверяли контрольную сумму входящих сообщений и проверяли, чтобы их контрольная сумма соответствовала контрольной сумме кворума.
Встраивайте в свою систему как можно больше других внутренних проверок, заставляя узлы, которые обнаруживают свои собственные ошибки, перезагружаться (это лучше, чем выполнять половину работы, если у вас достаточно узлов). Попытайтесь позволить им полностью удалить себя из кворума во время перезагрузки на случай, если они больше не появятся. При перезагрузке попросите их проверить контрольную сумму образа программного обеспечения (и всего остального, что они загружают) и выполнить полный тест ОЗУ, прежде чем снова ввести себя в кворум.
Используйте оборудование, чтобы поддержать вас, но делайте это осторожно. Например, вы можете получить оперативную память ECC и регулярно читать и записывать ее для исправления ошибок ECC (и паники, если ошибка не исправима). Однако (из памяти) статическое ОЗУ гораздо более терпимо к ионизирующему излучению, чем ДРАМ, во-первых, поэтому вместо него может быть лучше использовать статический ДРАМ. Смотрите также первый пункт в разделе «что бы я не делал».
Допустим, у вас есть 1% шанс отказа любого узла в течение одного дня, и давайте представим, что вы можете сделать отказы полностью независимыми. С 5 узлами вам понадобится три для отказа в течение одного дня, что составляет вероятность 0,0000%. С большим, ну, вы поняли.
Что бы я не делал:
Недооценивайте ценность отсутствия проблемы, с которой нужно начинать. Если вес не является проблемой, большой кусок металла вокруг вашего устройства будет гораздо более дешевым и надежным решением, чем команда программистов. Опто-оптическая связь входов электромагнитных помех является проблемой и т. Д. Как бы то ни было, попытайтесь при поиске ваших компонентов найти те компоненты, которые наилучшим образом защищены от ионизирующего излучения.
Бросайте свои собственные алгоритмы . Люди делали это раньше. Используйте их работу. Отказоустойчивость и распределенные алгоритмы сложны. Используйте работу других людей, где это возможно.
Используйте сложные настройки компилятора, надеясь, что вы обнаружите больше сбоев. Если вам повезет, вы можете обнаружить больше сбоев. Скорее всего, вы будете использовать путь кода в компиляторе, который был менее протестирован, особенно если вы сами его свернули.
Используйте методы, которые не были протестированы в вашей среде. Большинству людей, пишущих программное обеспечение высокой доступности, приходится имитировать режимы сбоев, чтобы проверить правильность их работы, и в результате пропустить множество режимов сбоев. Вы находитесь в «счастливом» положении с частыми неудачами по требованию. Поэтому протестируйте каждую технику и убедитесь, что ее применение действительно улучшает MTBF на величину, которая превышает сложность ее внедрения (со сложностью возникают ошибки). Особенно примените это к моим советам по алгоритмам кворума и т. Д.
источник
Поскольку вы специально запрашиваете программные решения и используете C ++, почему бы не использовать перегрузку операторов для создания собственных, безопасных типов данных? Например:
Вместо использования
uint32_t
(иdouble
, иint64_t
т.д.), сделать свой собственный ,SAFE_uint32_t
который содержит несколько (минимум 3) uint32_t. Перегрузите все операции, которые вы хотите (* + - / << >> = ==! = И т. Д.), И заставьте перегруженные операции выполнять независимо для каждого внутреннего значения, т.е. не делайте этого один раз и скопируйте результат. Как до, так и после убедитесь, что все внутренние значения совпадают. Если значения не совпадают, вы можете обновить неправильное значение до наиболее распространенного. Если наиболее распространенное значение отсутствует, вы можете смело уведомлять об ошибке.Таким образом, не имеет значения, произойдет ли повреждение в ALU, регистрах, ОЗУ или на шине, у вас все равно будет несколько попыток и очень хороший шанс на обнаружение ошибок. Однако обратите внимание, что это работает только для переменных, которые вы можете заменить - например, ваш указатель стека все еще будет восприимчивым.
Дополнительная история: я столкнулся с аналогичной проблемой, также на старом чипе ARM. Это оказался набор инструментов, который использовал старую версию GCC, которая вместе с конкретным чипом, который мы использовали, вызывала ошибку в определенных крайних случаях, которая (иногда) повреждала значения, передаваемые в функции. Убедитесь, что у вашего устройства нет проблем, прежде чем обвинять его в радиоактивности, и да, иногда это ошибка компилятора =)
источник
Отказ от ответственности: я не профессионал радиоактивности, и не работал на такого рода приложения. Но я работал над мягкими ошибками и избыточностью для долгосрочного архивирования критических данных, которые несколько связаны (одна и та же проблема, разные цели).
Основная проблема с радиоактивностью, на мой взгляд, заключается в том, что радиоактивность может переключать биты, поэтому радиоактивность может / повлияет на любую цифровую память . Эти ошибки обычно называются мягкими ошибками , битой гнилью и т.д.
Тогда возникает вопрос: как надежно вычислить, когда ваша память ненадежна?
Чтобы значительно снизить частоту мягких ошибок (за счет вычислительных затрат, поскольку это будут в основном программные решения), вы можете:
полагаться на старую старую схему избыточности и, в частности, на более эффективные коды с исправлением ошибок (те же цели, но более умные алгоритмы, чтобы вы могли восстановить больше битов с меньшей избыточностью). Это иногда (ошибочно) также называется контрольной суммой. При таком решении вы должны будете в любой момент сохранить полное состояние вашей программы в главной переменной / классе (или структуре?), Вычислить ECC и проверить правильность ECC, прежде чем что-либо делать, и если нет, починить поля. Это решение, однако, не гарантирует, что ваше программное обеспечение может работать (просто, что оно будет работать правильно, когда оно может, или перестает работать, если нет, потому что ECC может сказать вам, если что-то не так, и в этом случае вы можете остановить свое программное обеспечение, чтобы вы могли не получить поддельные результаты).
или вы можете использовать устойчивые алгоритмические структуры данныхЭто гарантирует, до некоторой степени, что ваша программа будет по-прежнему давать правильные результаты даже при наличии мягких ошибок. Эти алгоритмы можно рассматривать как смесь общих алгоритмических структур со схемами ECC, изначально встроенными в него, но это гораздо более гибко, чем это, потому что схема отказоустойчивости тесно связана со структурой, так что вам не нужно кодировать дополнительные процедуры. проверить ECC, и обычно они намного быстрее. Эти структуры обеспечивают способ гарантировать, что ваша программа будет работать при любых условиях, вплоть до теоретической границы мягких ошибок. Вы также можете смешать эти отказоустойчивые структуры со схемой избыточности / ECC для дополнительной безопасности (или закодировать ваши наиболее важные структуры данных как отказоустойчивые, а остальные - расходуемые данные, которые вы можете пересчитать из основных структур данных,
Если вы заинтересованы в устойчивых структурах данных (что является недавним, но интересным, новым направлением в алгоритмике и разработке избыточности), я советую вам прочитать следующие документы:
Интеллектуальные алгоритмы структуры данных, введение Джузеппе Ф.Итальяно, Университет Ромы "Tor Vergata"
Christiano, P., Demaine, ED, & Kishore, S. (2011). Безотказные отказоустойчивые структуры данных с аддитивными издержками. В алгоритмах и структурах данных (стр. 243-254). Springer Berlin Heidelberg.
Ferraro-Petrillo, U., Grandoni, F. & Italiano, GF (2013). Структуры данных, устойчивые к сбоям памяти: экспериментальное изучение словарей. Журнал экспериментальной алгоритмики (JEA), 18, 1-6.
Итальяно, GF (2010). Эластичные алгоритмы и структуры данных. В алгоритмах и сложности (стр. 13-24). Springer Berlin Heidelberg.
Если вам интересно узнать больше о области упругих структур данных, вы можете ознакомиться с работами Джузеппе Ф. Итальяно (и пройтись по ссылкам) и моделью Faulty-RAM (представленной в Finocchi et al. 2005; Finocchi). и итальяно 2008).
/ РЕДАКТИРОВАТЬ: я проиллюстрировал предотвращение / восстановление от программных ошибок в основном для оперативной памяти и хранения данных, но я не говорил об ошибках вычислений (ЦП) . Другие ответы уже указывали на использование атомарных транзакций, как в базах данных, поэтому я предложу другую, более простую схему: избыточность и большинство голосов .
Идея состоит в том, что вы просто делаете x раз одно и то же вычисление для каждого вычисления, которое вам нужно сделать, и сохраняете результат в x разных переменных (с x> = 3). Затем вы можете сравнить ваши переменные x :
Эта схема резервирования очень быстрая по сравнению с ECC (практически O (1)) и дает вам четкий сигнал, когда вам необходимо обеспечить отказоустойчивость . Кроме того, гарантируется, что большинство голосов (почти) никогда не произведут искаженный вывод, а также восстановятся после незначительных ошибок вычислений , потому что вероятность того, что вычисления x дадут тот же результат, бесконечно мала (поскольку существует огромное количество возможных выходных данных, почти невозможно случайным образом получить 3 раза то же самое, еще меньше шансов, если x> 3).
Таким образом, большинством голосов вы защищены от искаженного вывода, а с избыточностью x == 3 вы можете восстановить 1 ошибку (при x == 4 это будет 2 ошибки, которые можно исправить, и т. Д. - точное уравнение - это
nb_error_recoverable == (x-2)
где x - это число повторений расчетов, потому что вам нужно, по крайней мере, 2 согласованных расчета для восстановления с использованием большинства голосов).Недостаток заключается в том, что вам нужно вычислять x раз вместо одного, поэтому у вас есть дополнительные вычислительные затраты, но при этом линейная сложность, так что асимптотически вы не теряете много из-за преимуществ, которые вы получаете. Быстрый способ сделать большинство голосов - это вычислить режим для массива, но вы также можете использовать медианный фильтр.
Кроме того, если вы хотите убедиться, что вычисления выполняются правильно, если вы можете сделать свое собственное оборудование, вы можете собрать свое устройство с x процессорами и подключить систему так, чтобы вычисления автоматически дублировались на x процессорах с большинством голосов. механически в конце (например, с использованием вентилей AND / OR). Это часто реализуется в самолетах и критически важных устройствах (см. Тройное модульное резервирование ). Таким образом, у вас не будет никаких вычислительных накладных расходов (поскольку дополнительные вычисления будут выполняться параллельно), и у вас будет еще один уровень защиты от мягких ошибок (поскольку дублирование вычислений и большинство голосов будут управляться непосредственно аппаратным обеспечением, а не программное обеспечение - которое может быть легко повреждено, поскольку программа - это просто биты, хранящиеся в памяти ...).
источник
Один момент никто, кажется, не упомянул. Вы говорите, что разрабатываете в GCC и кросс-компилируете в ARM. Откуда вы знаете, что у вас нет кода, который делает предположения о свободной оперативной памяти, целочисленном размере, размере указателя, сколько времени требуется для выполнения определенной операции, сколько времени система будет работать непрерывно или что-то в этом роде? Это очень распространенная проблема.
Ответом обычно является автоматическое юнит-тестирование. Напишите тестовые наборы, которые осуществляют код в системе разработки, затем запустите те же тестовые наборы в целевой системе. Ищите различия!
Также проверьте наличие ошибок на встроенном устройстве. Вы можете обнаружить, что есть что-то вроде «не делайте этого, потому что это даст сбой, поэтому включите эту опцию компилятора, и компилятор будет обходить ее».
Короче говоря, ваш наиболее вероятный источник сбоев - это ошибки в вашем коде. Пока вы не уверены, что это не так, не беспокойтесь (пока) о более эзотерических режимах сбоев.
источник
Вы хотите 3+ подчиненных машин с хозяином вне радиационной среды. Все операции ввода / вывода проходят через мастер, который содержит механизм голосования и / или повторных попыток. У ведомых устройств должен быть аппаратный сторожевой таймер, и призыв к их усилению должен быть окружен CRC или чем-то подобным, чтобы уменьшить вероятность непроизвольного столкновения. Удар должен контролироваться ведущим, поэтому потерянное соединение с главным равняется перезагрузке в течение нескольких секунд.
Одним из преимуществ этого решения является то, что вы можете использовать тот же API для ведущего устройства, что и для ведомых устройств, поэтому избыточность становится прозрачной функцией.
Изменить: Из комментариев я чувствую необходимость прояснить "идею CRC". Вероятность того, что ведомое устройство ударит свой собственный сторожевой таймер, близка к нулю, если вы окружите удар с помощью CRC или дайджест-проверок случайных данных от мастера. Эти случайные данные отправляются только от ведущего, когда подчиненное устройство находится под контролем других. Случайные данные и CRC / дайджест немедленно очищаются после каждого удара. Частота удара ведущий-ведомый должна быть более чем в два раза больше времени ожидания сторожевого таймера. Данные, отправленные мастером, генерируются уникальным образом каждый раз.
источник
Как насчет запуска многих экземпляров вашего приложения. Если сбои происходят из-за случайных битовых изменений памяти, есть вероятность, что некоторые экземпляры вашего приложения справятся с этим и дадут точные результаты. Вероятно, довольно просто (для кого-то со статистическим прошлым) подсчитать, сколько экземпляров вам нужно, учитывая вероятность битового флопа, чтобы получить как можно меньшую общую погрешность.
источник
То, что вы спрашиваете, является довольно сложной темой - не так легко ответить. Другие ответы в порядке, но они охватывают лишь небольшую часть всего, что вам нужно сделать.
Как видно из комментариев , невозможно решить проблемы с оборудованием на 100%, однако возможно с высокой вероятностью уменьшить или отловить их, используя различные методы.
Если бы я был вами, я бы создал программное обеспечение самого высокого уровня безопасности (SIL-4). Получите документ МЭК 61513 (для атомной промышленности) и следуйте ему.
источник
Кто-то упомянул об использовании более медленных чипов, чтобы ионы не могли легко перевернуть биты. Аналогичным образом возможно использовать специализированный процессор / оперативную память, который на самом деле использует несколько битов для хранения одного бита. Таким образом, обеспечивая аппаратную отказоустойчивость, так как маловероятно, что все биты будут перевернуты. Так что 1 = 1111, но нужно было бы попасть 4 раза, чтобы на самом деле перевернуться. (4 может быть плохим числом, так как если 2 бита перевернуты, это уже неоднозначно). Таким образом, если вы выберете 8, вы получите в 8 раз меньше оперативной памяти и на некоторое время меньше времени доступа, но гораздо более надежное представление данных. Вероятно, вы могли бы сделать это как на уровне программного обеспечения с помощью специализированного компилятора (выделите на x больше места для всего), так и на языке реализации (напишите обертки для структур данных, которые размещают объекты таким образом).
источник
Возможно, было бы полезно узнать, означает ли это, что аппаратное обеспечение «предназначено для этой среды». Как это исправить и / или указывает на наличие ошибок SEU?
В одном проекте, связанном с исследованием космоса, у нас был собственный MCU, который вызывал бы исключение / прерывание при ошибках SEU, но с некоторой задержкой, то есть некоторые циклы могут проходить / выполнять инструкции после одного insn, который вызвал исключение SEU.
Особенно уязвим был кэш данных, поэтому обработчик лишил законной силы строку кэша и перезапустил программу. Только то, что из-за неточного характера исключения последовательность insns во главе с повышением исключения может не быть перезапускаемой.
Мы определили опасные (не перезапускаемые) последовательности (например
lw $3, 0x0($2)
, за которым следует insn, который изменяет$2
и не зависит от данных$3
), и я внес изменения в GCC, поэтому такие последовательности не возникают (например, в качестве крайней меры, разделяя два insns на аnop
).Просто кое-что рассмотреть ...
источник
Если ваше оборудование выходит из строя, вы можете использовать механическое хранилище для его восстановления. Если ваша кодовая база мала и имеет некоторое физическое пространство, вы можете использовать механическое хранилище данных.
Там будет поверхность материала, которая не будет подвергаться воздействию радиации. Несколько передач будет там. Механический считыватель будет работать на всех передачах и будет гибким, чтобы двигаться вверх и вниз. Вниз означает, что это 0, а вверх означает, что это 1. Из 0 и 1 вы можете сгенерировать свою кодовую базу.
источник
Используйте циклический планировщик . Это дает вам возможность добавлять регулярное время обслуживания для проверки правильности критических данных. Чаще всего встречается проблема с повреждением стека. Если ваше программное обеспечение циклично, вы можете переинициализировать стек между циклами. Не используйте повторно стеки для вызовов прерывания, установите отдельный стек для каждого важного вызова прерывания.
Похожий на Watchdog концепт является таймерами крайних сроков. Запустите аппаратный таймер перед вызовом функции. Если функция не возвращается до того, как таймер крайнего срока прерывается, перезагрузите стек и попробуйте снова. Если после 3/5 попыток это не помогло, необходимо перезагрузить ПЗУ.
Разделите ваше программное обеспечение на части и изолируйте эти части, чтобы использовать отдельные области памяти и время выполнения (особенно в среде управления). Пример: получение сигнала, предварительная обработка данных, основной алгоритм и реализация / передача результата. Это означает, что сбой в одной части не приведет к сбоям в остальной части программы. Поэтому, пока мы восстанавливаем получение сигнала, остальные задачи продолжаются на устаревших данных.
Все нуждается в CRC. Если вы выполняете из ОЗУ, даже ваш .text нуждается в CRC. Регулярно проверяйте CRC, если вы используете циклический планировщик. Некоторые компиляторы (не GCC) могут генерировать CRC для каждого раздела, а некоторые процессоры имеют выделенное оборудование для выполнения вычислений CRC, но я думаю, что это выходит за рамки вашего вопроса. Проверка CRC также предлагает контроллеру ECC в памяти исправить однобитовые ошибки, прежде чем это станет проблемой.
источник
Во-первых, разработайте свое приложение на случай отказа . Убедитесь, что как часть нормальной работы потока он ожидает сброса (в зависимости от вашего приложения и типа сбоя: мягкий или жесткий). Этого трудно достичь идеальным: критические операции, требующие некоторой степени транзакционности, могут нуждаться в проверке и настройке на уровне сборки, чтобы прерывание в ключевой точке не могло привести к несовместимым внешним командам. Сбой быстро, как только обнаруживается любое неисправимое повреждение памяти или отклонение потока управления. Журнал сбоев, если это возможно.
Во-вторых, где это возможно, исправьте коррупцию и продолжайте . Это означает, что контрольные суммы и исправление постоянных таблиц (и программного кода, если вы можете) часто; возможно, перед каждой основной операцией или по временному прерыванию, и сохраняя переменные в структурах, которые автозаменяются (снова перед каждой основной операцией или по временному прерыванию берут большинство голосов от 3 и исправляют, если это одно отклонение). Зарегистрируйте исправления, если это возможно.
В-третьих, провал теста . Установите воспроизводимую тестовую среду, которая перебрасывает биты в памяти псевдослучайно. Это позволит вам воспроизвести коррупционные ситуации и помочь разработать приложение вокруг них.
источник
Учитывая комментарии суперкатера, тенденции современных компиляторов и другие вещи, я бы соблазнился вернуться к древним временам и писать весь код в ассемблере и статическом распределении памяти везде. Для такого рода полной надежности я думаю, что сборка больше не несет большой процентной разницы в стоимости.
источник
Здесь огромное количество ответов, но я постараюсь обобщить свои идеи по этому поводу.
Что-то выходит из строя или работает неправильно, может быть результатом ваших собственных ошибок - тогда это должно быть легко исправить, когда вы обнаружите проблему. Но существует также возможность аппаратных сбоев - и это сложно, если не невозможно, исправить в целом.
Я бы порекомендовал сначала попытаться поймать проблемную ситуацию, ведя журнал (стек, регистры, вызовы функций) - либо записывая их где-то в файл, либо передавая их каким-либо образом напрямую («о нет, я сбой»).
Восстановление из такой ситуации ошибки - это либо перезагрузка (если программное обеспечение все еще живо и работает), либо аппаратный сброс (например, hw watchdogs). Проще начать с первого.
Если проблема связана с аппаратным обеспечением - тогда регистрация должна помочь вам определить, в каких случаях происходит вызов функции, и это может дать вам внутреннюю информацию о том, что не работает и где.
Также, если код относительно сложен - имеет смысл «разделить и завоевать» его - то есть вы удаляете / отключаете некоторые вызовы функций там, где вы подозреваете проблему - как правило, отключая половину кода и включая другую половину - вы можете получить «работает» / решение типа «не работает», после которого вы можете сосредоточиться на другой половине кода. (Где проблема)
Если через какое-то время возникает проблема - тогда можно ожидать переполнения стека - тогда лучше отслеживать регистры точек стека - если они постоянно растут.
И если вам удастся полностью свести к минимуму свой код до тех пор, пока приложение типа «здравствуй, мир» - и оно все равно будет случайным образом терпеть неудачу - тогда возникнут проблемы с оборудованием - и потребуется «обновление оборудования» - то есть изобрести такой процессор / ram / ... - комбинация аппаратных средств, которая лучше переносит излучение.
Вероятно, наиболее важным является то, как вы возвращаете свои журналы, если машина полностью остановлена / перезагружена / не работает - вероятно, первое, что должен сделать загрузчик, - это вернитесь домой, если возникнет проблемная ситуация.
Если в вашей среде возможно также передавать сигнал и получать ответ - вы можете попытаться создать какую-то среду удаленной отладки в режиме онлайн, но тогда у вас должен быть хотя бы работающий коммуникационный носитель и какой-нибудь процессор / некоторое количество оперативной памяти в рабочем состоянии. И под удаленной отладкой я подразумеваю либо подход типа GDB / gdb-заглушки, либо вашу собственную реализацию того, что вам нужно вернуть из приложения (например, загрузка файлов журнала, загрузка стека вызовов, загрузка оперативной памяти, перезапуск)
источник
Я действительно прочитал много хороших ответов!
Вот мои 2 цента: построить статистическую модель аномалий памяти / регистра, написав программное обеспечение для проверки памяти или частых сравнений регистров. Кроме того, создайте эмулятор в стиле виртуальной машины, где вы сможете поэкспериментировать с этой проблемой. Я предполагаю, что если вы измените размер перехода, тактовую частоту, производителя, корпус и т. Д., То наблюдаете другое поведение.
Даже память нашего настольного ПК имеет определенную частоту отказов, которая, однако, не мешает повседневной работе.
источник