Question Compiler une application pour une utilisation dans des environnements hautement radioactifs


Nous compilons une application C / C ++ intégrée qui est déployée dans un périphérique blindé dans un environnement bombardé avec rayonnement ionisant. Nous utilisons GCC et la compilation croisée pour ARM. Une fois déployée, notre application génère des données erronées et se bloque plus souvent que nous le souhaiterions. Le matériel est conçu pour cet environnement et notre application fonctionne sur cette plate-forme depuis plusieurs années.

Y a-t-il des changements que nous pouvons apporter à notre code, ou des améliorations à la compilation qui peuvent être faites pour identifier / corriger erreurs souples et la corruption de la mémoire causée par des événements uniques? Est-ce que d'autres développeurs ont réussi à réduire les effets néfastes des erreurs logicielles sur une application de longue durée?


1278
2018-04-24 19:09


origine


Réponses:


Travailler pendant environ 4-5 ans avec le développement de logiciels / micrologiciels et les tests d'environnement de satellites miniaturisés*, Je voudrais partager mon expérience ici.

* (les satellites miniaturisés sont beaucoup plus sujets à des perturbations d'événements uniques que les satellites plus grands en raison de ses tailles relativement petites et limitées pour ses composants électroniques)

Pour être très concis et direct: il n'y a pas de mécanisme pour récupérer détectable, erroné   situation par le logiciel / firmware lui-même sans pour autant, au moins un    copie de version de travail minimum du logiciel / firmware quelque part pour récupération but - et avec le matériel supportant la récupération (fonctionnel).

Maintenant, cette situation est normalement gérée à la fois au niveau matériel et logiciel. Ici, comme vous le demandez, je vais partager ce que nous pouvons faire au niveau du logiciel.

  1. ... but de récupération .... Fournir la capacité de mettre à jour / recompiler / reflasher votre logiciel / firmware dans un environnement réel. C'est un presque indispensable fonctionnalité pour tout logiciel / firmware dans un environnement fortement ionisé. Sans cela, vous pourrait avoir un logiciel / matériel redondant autant que vous voulez mais à un moment donné, ils vont tous exploser. Alors, préparez cette fonctionnalité!

  2. ... version de travail minimum ... Avoir responsive, plusieurs copies, version minimale du logiciel / firmware dans votre code. C'est comme le mode sans échec dans Windows. Au lieu d'avoir une seule version entièrement fonctionnelle de votre logiciel, ayez plusieurs copies de la version minimale de votre logiciel / micrologiciel. La copie minimum aura généralement beaucoup moins de taille que la copie complète et aura presque toujours seulement les deux ou trois caractéristiques suivantes:

    1. capable d'écouter la commande du système externe,
    2. capable de mettre à jour le logiciel / firmware actuel,
    3. capable de surveiller les données d'entretien de l'opération de base.
  3. ... copie ... quelque part ... Avoir un logiciel / firmware redondant quelque part.

    1. Vous pourriez, avec ou sans matériel redondant, essayez d'avoir un logiciel / firmware redondant dans votre ARM uC. Ceci est normalement fait en ayant deux ou plusieurs logiciels / micrologiciels identiques dans des adresses séparées qui envoie le battement de coeur l'un à l'autre - mais un seul sera actif à la fois. Si un ou plusieurs logiciels / micrologiciels ne répondent plus, passez à l'autre logiciel / micrologiciel. L'avantage d'utiliser cette approche est que nous pouvons avoir un remplacement fonctionnel immédiatement après une erreur - sans aucun contact avec un système / une partie externe responsable de la détection et de la réparation de l'erreur (dans le cas satellite) MCC)).

      Strictement parlant, sans matériel redondant, l'inconvénient de le faire est vous réellement ne peux pas éliminer tout point unique de défaillance. À tout le moins, vous aurez toujours un point de défaillance unique, qui est l'interrupteur lui-même (ou souvent le début du code). Néanmoins, pour un dispositif limité par sa taille dans un environnement fortement ionisé (tels que les satellites pico / femto), la réduction du point unique de défaillance à un point sans pour autant matériel supplémentaire vaudra encore la peine d'être considéré. Somemore, le code de la commutation serait certainement beaucoup moins que le code pour l'ensemble du programme - ce qui réduit considérablement le risque d'obtenir un événement unique.

    2. Mais si vous ne le faites pas, vous devriez avoir au moins une copie dans votre système externe qui peut entrer en contact avec l'appareil et mettre à jour le logiciel / firmware (dans le cas du satellite, c'est encore le centre de contrôle de mission).

    3. Vous pouvez également avoir la copie dans votre mémoire permanente de votre appareil qui peut être déclenchée pour restaurer le logiciel / micrologiciel du système en cours d'exécution
  4. ... situation erronée détectable .. L'erreur doit être détectable, généralement par le matériel correction d'erreurs / circuit de détection ou par un petit morceau de code pour la correction / détection d'erreur. Il est préférable de mettre un tel code petit, multiple et indépendant à partir du logiciel / firmware principal. Sa tâche principale est seulement pour vérifier / corriger. Si le circuit matériel / firmware est fiable (comme il est plus durci par le rayonnement que les silences - ou ayant plusieurs circuits / logiques), alors vous pourriez envisager d'effectuer une correction d'erreur avec celui-ci. Mais si ce n'est pas le cas, il est préférable de le faire en tant que détection d'erreur. La correction peut être effectuée par un système / appareil externe. Pour la correction d'erreur, vous pouvez envisager d'utiliser un algorithme de base de correction d'erreur comme Hamming / Golay23, car ils peuvent être implémentés plus facilement dans le circuit / logiciel. Mais cela dépend en fin de compte des capacités de votre équipe. Pour la détection d'erreur, normalement CRC est utilisé.

  5. ... matériel supportant la récupération Maintenant, vient à l'aspect le plus difficile sur cette question. En fin de compte, la récupération nécessite le matériel qui est responsable de la récupération d'être au moins fonctionnel. Si le matériel est brisé en permanence (normalement après son Dose ionisante totale atteint un certain niveau), alors il n'y a (malheureusement) aucun moyen pour le logiciel d'aider à la récupération. Ainsi, le matériel est à juste titre la préoccupation la plus importante pour un appareil exposé à un niveau de rayonnement élevé (tel qu'un satellite).

En plus de la suggestion ci-dessus pour anticiper l'erreur du micrologiciel due à un seul événement perturbé, je voudrais également vous suggérer d'avoir:

  1. Algorithme de détection d'erreur et / ou de correction d'erreur dans le protocole de communication inter-sous-système. Ceci est un autre presque doit avoir afin d'éviter les signaux incomplets / faux reçus de l'autre système

  2. Filtrer dans votre lecture ADC. Faire ne pas utiliser la lecture ADC directement. Filtrer par filtre médian, filtre moyen, ou tout autre filtre - jamais faire confiance à la valeur de lecture unique. Échantillon plus, pas moins - raisonnablement.


729
2018-04-25 02:58



La NASA a un document sur le durcissement par rayonnement Logiciel. Il décrit trois tâches principales:

  1. Surveillance régulière de la mémoire pour détecter les erreurs, puis éliminer ces erreurs,
  2. mécanismes robustes de récupération d'erreur, et
  3. la possibilité de reconfigurer si quelque chose ne fonctionne plus.

Notez que le taux de balayage de la mémoire doit être suffisamment fréquent pour que les erreurs multi-bits se produisent rarement, comme la plupart ECC la mémoire peut récupérer des erreurs à un seul bit, pas des erreurs à plusieurs bits.

La reprise d'erreur robuste inclut le transfert de flux de contrôle (généralement le redémarrage d'un processus à un point situé avant l'erreur), la libération de ressources et la restauration de données.

Leur principale recommandation pour la restauration de données est d'en éviter le besoin, en faisant en sorte que les données intermédiaires soient traitées comme temporaires, de sorte qu'un redémarrage avant l'erreur ramène également les données à un état fiable. Cela ressemble à la notion de «transactions» dans les bases de données.

Ils discutent des techniques particulièrement adaptées aux langages orientés objet tels que C ++. Par exemple

  1. ECC basés sur des logiciels pour des objets mémoire contigus
  2. Programmation par contrat: vérification des conditions préalables et postconditions, puis vérification de l'objet pour vérifier qu'il est toujours dans un état valide.

370
2018-04-24 19:32



Voici quelques pensées et idées:

Utilisez la ROM de manière plus créative.

Rangez tout ce que vous pouvez en ROM. Au lieu de calculer des choses, stockez les tables de consultation dans la ROM. (Assurez-vous que votre compilateur sort vos tables de consultation dans la section en lecture seule! Imprimez les adresses de la mémoire à l'exécution pour vérifier!) Stockez votre table de vecteurs d'interruption dans la ROM. Bien sûr, effectuez des tests pour voir à quel point votre ROM est fiable par rapport à votre RAM.

Utilisez votre meilleure RAM pour la pile.

Les UES dans la pile sont probablement la source la plus probable de plantages, car c'est là que vivent généralement les variables d'index, les variables d'état, les adresses de retour et les pointeurs de différentes sortes.

Implémentez les routines timer-tick et watchdog timer.

Vous pouvez exécuter une routine "vérification de cohérence" à chaque coche de la minuterie, ainsi qu'une routine de surveillance pour gérer le verrouillage du système. Votre code principal pourrait également incrémenter périodiquement un compteur pour indiquer la progression, et la routine de vérification de la santé mentale pourrait garantir que cela s'est produit.

Mettre en place codes de correction d'erreur dans le logiciel.

Vous pouvez ajouter de la redondance à vos données pour pouvoir détecter et / ou corriger les erreurs. Cela ajoutera du temps de traitement, ce qui pourrait exposer le processeur à des radiations plus longtemps, ce qui augmentera le risque d'erreurs. Vous devez donc envisager le compromis.

Rappelez-vous les caches.

Vérifiez la taille de vos caches CPU. Les données que vous avez récemment consultées ou modifiées seront probablement dans un cache. Je crois que vous pouvez désactiver au moins une partie des caches (à un coût de performance élevé); Vous devriez essayer ceci pour voir à quel point les caches sont sensibles aux SEU. Si les caches sont plus robustes que la RAM, vous pouvez lire et réécrire régulièrement les données critiques pour vous assurer qu'elles restent dans le cache et ramener la RAM en ligne.

Utilisez intelligemment les gestionnaires de défauts de page.

Si vous marquez une page de mémoire comme non présente, la CPU émet une erreur de page lorsque vous essayez d'y accéder. Vous pouvez créer un gestionnaire d'erreurs de page qui effectue une vérification avant de traiter la demande de lecture. (Les systèmes d'exploitation PC l'utilisent pour charger de manière transparente les pages qui ont été échangées sur le disque.)

Utilisez le langage d'assemblage pour les choses critiques (ce qui pourrait être tout).

Avec le langage d'assemblage, vous connaître ce qui est dans les registres et ce qui est dans la RAM; toi connaître quelles tables de RAM spéciales le processeur utilise, et vous pouvez concevoir des choses de manière détournée pour réduire vos risques.

Utilisation objdump pour réellement regarder le langage d'assemblage généré, et pour déterminer combien de code chacune de vos routines prend.

Si vous utilisez un gros système d'exploitation comme Linux, vous demandez des problèmes; il y a tellement de complexité et tant de choses qui vont mal.

Rappelez-vous que c'est un jeu de probabilités.

Un commentateur a dit

Chaque routine que vous écrivez pour attraper des erreurs sera sujette à l'échec de la même cause.

Tandis que ceci est vrai, les chances d'erreurs dans le (disons) 100 octets de code et de données requis pour qu'une routine de contrôle fonctionne correctement est beaucoup plus petite que le risque d'erreurs ailleurs. Si votre ROM est assez fiable et presque tout le code / données est réellement dans ROM, vos chances sont encore meilleures.

Utilisez du matériel redondant.

Utilisez 2 configurations matérielles identiques ou plus avec un code identique. Si les résultats diffèrent, une réinitialisation doit être déclenchée. Avec 3 appareils ou plus, vous pouvez utiliser un système de "vote" pour essayer d'identifier lequel a été compromis.


107
2018-04-24 23:11



Vous pouvez également être intéressé par la riche littérature sur le sujet de la tolérance aux fautes algorithmique. Cela inclut l'ancienne affectation: écrire une sorte qui trie correctement son entrée lorsqu'un nombre constant de comparaisons échouera (ou, la version légèrement plus mauvaise, quand le nombre asymptotique de comparaisons échouées échelles comme log(n) pour n comparaisons).

Un endroit pour commencer à lire est le document de 1984 de Huang et Abraham "Tolérance aux pannes basée sur l'algorithme pour les opérations matriciellesLeur idée est vaguement similaire au calcul crypté homomorphique (mais ce n'est pas vraiment la même chose, puisqu'ils tentent la détection / correction d'erreur au niveau de l'opération).

Un descendant plus récent de ce document est Bosilca, Delmas, Dongarra et Langou "Tolérance de panne basée sur l'algorithme appliquée au calcul haute performance".


93
2018-04-24 21:13



L'écriture de code pour les environnements radioactifs n'est pas vraiment différente de l'écriture de code pour toute application critique.

En plus de ce qui a déjà été mentionné, voici quelques conseils divers:

  • Utilisez les mesures de sécurité quotidiennes «pain et beurre» qui devraient être présentes sur tout système embarqué semi-professionnel: chien de garde interne, détecteur de basse tension interne, moniteur d'horloge interne. Ces choses ne devraient même pas être mentionnées en 2016 et elles sont standard sur presque tous les microcontrôleurs modernes.
  • Si vous avez un MCU de sécurité et / ou orienté vers l'automobile, il aura certaines fonctions de surveillance, telles qu'une fenêtre temporelle donnée, à l'intérieur desquelles vous devez actualiser le chien de garde. Ceci est préférable si vous avez un système temps réel critique.
  • En général, utilisez un microcontrôleur adapté à ce type de système, et non une peluche générique que vous avez reçue dans un paquet de flocons de maïs. Presque tous les fabricants de microcontrôleurs ont des microcontrôleurs spécialisés conçus pour des applications de sécurité (TI, Freescale, Renesas, ST, Infineon, etc.). Ceux-ci ont beaucoup de fonctionnalités de sécurité intégrées, y compris les cœurs de verrouillage: ce qui signifie qu'il y a 2 cœurs de processeur exécutant le même code, et qu'ils doivent être en accord les uns avec les autres.
  • IMPORTANT: Vous devez vous assurer de l'intégrité des registres MCU internes. Tous les registres de contrôle et d'état des périphériques matériels qui sont inscriptibles peuvent être situés dans la mémoire RAM, et sont donc vulnérables.

    Pour vous protéger contre les corruptions de registre, de préférence, choisissez un microcontrôleur avec des fonctions intégrées "en écriture seule" des registres. En outre, vous devez stocker les valeurs par défaut de tous les registres matériels dans NVM et copier ces valeurs sur vos registres à intervalles réguliers. Vous pouvez assurer l'intégrité des variables importantes de la même manière.

    Remarque: utilisez toujours une programmation défensive. Ce qui signifie que vous devez configurer tout enregistre dans le MCU et pas seulement ceux utilisés par l'application. Vous ne voulez pas qu'un périphérique matériel aléatoire se réveille soudainement.

  • Il existe toutes sortes de méthodes pour vérifier les erreurs dans la RAM ou la NVM: checksums, "modèles de marche", ECC logiciel, etc. La meilleure solution est de ne pas en utiliser, mais d'utiliser un MCU avec ECC intégré et contrôles similaires. Parce que faire cela dans un logiciel est complexe, et la vérification d'erreur en elle-même pourrait donc introduire des bogues et des problèmes inattendus.

  • Utilisez la redondance. Vous pouvez stocker à la fois la mémoire volatile et non volatile dans deux segments "miroir" identiques, qui doivent toujours être équivalents. Chaque segment peut avoir une somme de contrôle CRC attachée.
  • Évitez d'utiliser des mémoires externes à l'extérieur de la MCU.
  • Implémentez une routine de service d'interruption par défaut / un gestionnaire d'exceptions par défaut pour toutes les interruptions / exceptions possibles. Même ceux que vous n'utilisez pas. La routine par défaut ne devrait rien faire sauf fermer sa propre source d'interruption.
  • Comprendre et adopter le concept de programmation défensive. Cela signifie que votre programme doit gérer tous les cas possibles, même ceux qui ne peuvent pas se produire en théorie. Exemples.

    Le microprogramme de haute qualité critique détecte autant d'erreurs que possible, puis les ignore de manière sûre.

  • N'écrivez jamais de programmes qui reposent sur un comportement mal spécifié. Il est probable qu'un tel comportement puisse changer radicalement avec des changements matériels inattendus causés par des radiations ou des interférences électromagnétiques. La meilleure façon de s'assurer que votre programme est libre de telles conneries est d'utiliser une norme de codage comme MISRA, avec un outil d'analyse statique. Cela aidera également à la programmation défensive et à l'élimination des bogues (pourquoi ne voudriez-vous pas détecter les bogues dans n'importe quel type d'application?).
  • IMPORTANT: n'implémentez pas les valeurs par défaut des variables de durée de stockage statique. Autrement dit, ne faites pas confiance au contenu par défaut de la .data ou .bss. Il pourrait y avoir un laps de temps entre le moment de l'initialisation et le moment où la variable est réellement utilisée, il y aurait eu beaucoup de temps pour que la RAM soit corrompue. Au lieu de cela, écrivez le programme afin que toutes ces variables soient définies à partir de NVM en cours d'exécution, juste avant l'heure à laquelle une telle variable est utilisée pour la première fois.

    En pratique, cela signifie que si une variable est déclarée à la portée du fichier ou static, vous ne devriez jamais utiliser = pour l'initialiser (ou vous pourriez, mais c'est inutile, parce que vous ne pouvez pas compter sur la valeur de toute façon). Réglez-le toujours en cours d'exécution, juste avant l'utilisation. S'il est possible de mettre à jour de manière répétée de telles variables à partir de NVM, faites-le.

    De même en C ++, ne comptez pas sur les constructeurs pour les variables de durée de stockage statique. Demandez au (x) constructeur (s) d'appeler une routine "d'installation" publique, que vous pourrez également appeler plus tard en cours d'exécution, directement à partir de l'application de l'appelant.

    Si possible, supprimez le code de démarrage "copier-vers le bas" qui initialise .data et .bss (et appelle les constructeurs C ++) entièrement, de sorte que vous obtenez des erreurs de l'éditeur de liens si vous écrivez du code en s'appuyant sur tel. De nombreux compilateurs ont la possibilité d'ignorer cela, généralement appelé "démarrage minimal / rapide" ou similaire.

    Cela signifie que toutes les bibliothèques externes doivent être vérifiées afin qu'elles ne contiennent pas une telle dépendance.

  • Implémentez et définissez un état de sécurité pour le programme, à l'endroit où vous allez revenir en cas d'erreurs critiques.

  • La mise en œuvre d'un rapport d'erreur / système de journal des erreurs est toujours utile.

36
2018-04-27 14:11



Il peut être possible d'utiliser C pour écrire des programmes qui se comportent de manière robuste dans de tels environnements, mais seulement si la plupart des formes d'optimisation du compilateur sont désactivées. Les compilateurs d'optimisation sont conçus pour remplacer de nombreux modèles de codage apparemment redondants par des modèles "plus efficaces", et peuvent ne pas avoir la moindre idée de la raison pour laquelle le programmeur teste x==42 quand le compilateur sait qu'il n'y a pas moyen x pourrait éventuellement contenir autre chose parce que le programmeur veut empêcher l'exécution de certains code avec xdétenir une autre valeur - même dans les cas où le seul moyen de conserver cette valeur serait que le système reçoive une sorte de pépin électrique.

Déclarer des variables comme volatile est souvent utile, mais peut ne pas être une panacée. Il est particulièrement important de noter que le codage de sécurité nécessite souvent opérations ont des verrouillages matériels qui nécessitent plusieurs étapes pour activer, et que le code soit écrit en utilisant le motif:

... code that checks system state
if (system_state_favors_activation)
{
  prepare_for_activation();
  ... code that checks system state again
  if (system_state_is_valid)
  {
    if (system_state_favors_activation)
      trigger_activation();
  }
  else
    perform_safety_shutdown_and_restart();
}
cancel_preparations();

Si un compilateur traduit le code de manière relativement littérale, et si tout les vérifications de l'état du système sont répétées après prepare_for_activation(), le système peut être robuste contre presque n'importe quel événement de plantage unique plausible, même ceux qui corrompraient arbitrairement le compteur et la pile du programme. Si un petit problème se produit juste après un appel à prepare_for_activation(), cela impliquerait cette activation aurait été appropriée (puisqu'il n'y a pas d'autre raison prepare_for_activation() aurait été appelé avant le pépin). Si la glitch provoque le code à atteindre prepare_for_activation() de manière inappropriée, mais ne sont pas des événements de glitch ultérieurs, il n'y aurait aucun moyen de code par la suite atteindre trigger_activation() sans avoir passé le contrôle de validation ou en appelant d'abord cancel_preparations [si la pile pépite, l'exécution peut se dérouler juste avant trigger_activation() après le contexte qui a appelé prepare_for_activation() retourne, mais l'appel à cancel_preparations() aurait eu lieu entre les appels à prepare_for_activation() et trigger_activation(), rendant ainsi le dernier appel inoffensif.

Un tel code peut être sûr en C traditionnel, mais pas avec les compilateurs C modernes. De tels compilateurs peuvent être très dangereux dans ce type d'environnement car agressifs, ils s'efforcent d'inclure uniquement du code qui sera pertinent dans des situations qui pourraient se produire via un mécanisme bien défini et dont les conséquences seraient également bien définies. Un code dont le but serait de détecter et de nettoyer après des échecs peut, dans certains cas, aggraver la situation. Si le compilateur détermine que la tentative de récupération invoquerait dans certains cas un comportement indéfini, il peut en déduire que les conditions qui nécessiteraient une telle récupération dans de tels cas ne peuvent pas se produire, éliminant ainsi le code qui les aurait vérifiées.


30
2018-04-25 16:14



C'est un sujet extrêmement large. Fondamentalement, vous ne pouvez pas vraiment récupérer de la corruption de la mémoire, mais vous pouvez au moins essayer de échouer rapidement. Voici quelques techniques que vous pourriez utiliser:

  • données constantes de somme de contrôle. Si vous avez des données de configuration qui restent constantes pendant une longue période (y compris les registres matériels que vous avez configurés), calculez sa somme de contrôle à l'initialisation et vérifiez-la périodiquement. Lorsque vous voyez une discordance, il est temps de réinitialiser ou de réinitialiser.

  • stocker les variables avec redondance. Si vous avez une variable importante x, écrivez sa valeur dans x1, x2 et x3 et lisez-le comme (x1 == x2) ? x2 : x3.

  • mettre en place surveillance du flux de programme. XOR un drapeau global avec une valeur unique dans les fonctions / branches importantes appelées de la boucle principale. L'exécution du programme dans un environnement sans rayonnement avec une couverture de test de près de 100% devrait vous donner la liste des valeurs acceptables du drapeau à la fin du cycle. Réinitialiser si vous voyez des écarts.

  • surveiller le pointeur de la pile. Au début de la boucle principale, comparez le pointeur de pile avec sa valeur attendue. Réinitialiser sur l'écart.


27
2018-04-25 17:05



Ce qui pourrait vous aider est un chien de garde. Les chiens de garde ont été largement utilisés dans l'informatique industrielle dans les années 1980. Les pannes matérielles étaient alors beaucoup plus fréquentes - une autre réponse se réfère également à cette période.

Un chien de garde est une fonctionnalité matérielle / logicielle combinée. Le matériel est un simple compteur qui décompte d'un nombre (disons 1023) à zéro. TTL ou une autre logique pourrait être utilisée.

Le logiciel a été conçu de telle sorte qu'une routine surveille le bon fonctionnement de tous les systèmes essentiels. Si cette routine se termine correctement = l'ordinateur fonctionne correctement, il remet le compteur à 1023.

La conception globale est telle que dans des circonstances normales, le logiciel empêche que le compteur matériel atteigne zéro. Dans le cas où le compteur atteint zéro, le matériel du compteur effectue sa tâche unique et réinitialise l'ensemble du système. Du point de vue du compteur, zéro est égal à 1024 et le compteur continue à compter à nouveau.

Ce chien de garde s'assure que l'ordinateur connecté est redémarré dans un grand nombre de cas d'échec. Je dois admettre que je ne suis pas familier avec le matériel qui est capable d'effectuer une telle fonction sur les ordinateurs d'aujourd'hui. Les interfaces avec le matériel externe sont maintenant beaucoup plus complexes qu'elles ne l'étaient auparavant.

Un inconvénient inhérent du chien de garde est que le système n'est pas disponible à partir du moment où il échoue jusqu'à ce que le compteur chien de garde atteigne le temps zéro + redémarrage. Bien que ce temps soit généralement beaucoup plus court que toute intervention externe ou humaine, l'équipement pris en charge devra être en mesure de procéder sans contrôle informatique pour cette période.


26
2018-04-26 22:41



Cette réponse suppose que vous êtes soucieux d'avoir un système qui fonctionne correctement, en plus d'avoir un système qui est un coût minimum ou rapide; la plupart des gens qui jouent avec des choses radioactives apprécient l'exactitude / la sécurité par rapport à la vitesse / au coût

Plusieurs personnes ont suggéré des changements de matériel que vous pouvez faire (bien - il y a déjà beaucoup de bonnes choses dans les réponses et je n'ai pas l'intention de les répéter), et d'autres ont suggéré la redondance (excellent en principe), mais je ne pense pas n'importe qui a suggéré comment cette redondance pourrait fonctionner dans la pratique. Comment échouez-vous? Comment savez-vous quand quelque chose a «mal tourné»? De nombreuses technologies fonctionnent sur la base de tout ce qui fonctionne, et l'échec est donc une chose délicate à gérer. Cependant, certaines technologies de calcul distribué conçues pour l'échelle attendre l'échec (après tout avec suffisamment d'échelle, la défaillance d'un nœud de plusieurs est inévitable avec tout MTBF pour un seul nœud); vous pouvez exploiter cela pour votre environnement.

Voici quelques idées:

  • Assurez-vous que tout votre matériel est répliqué n fois (où n est supérieur à 2, et de préférence impair), et que chaque élément matériel peut communiquer avec chaque autre élément matériel. Ethernet est un moyen évident de le faire, mais il existe de nombreuses autres routes beaucoup plus simples qui permettraient une meilleure protection (par exemple CAN). Minimiser les composants communs (même les alimentations). Cela peut vouloir dire échantillonner des entrées ADC à plusieurs endroits, par exemple.

  • Assurez-vous que l'état de votre application est au même endroit, par ex. dans une machine à états finis. Cela peut être entièrement basé sur la RAM, mais n'exclut pas le stockage stable. Il sera ainsi stocké à plusieurs endroits.

  • Adopter un protocole de quorum pour les changements d'état. Voir RADEAU par exemple. Comme vous travaillez en C ++, il existe des bibliothèques bien connues pour cela. Les modifications apportées au FSM ne seront effectuées que si la majorité des nœuds sont d'accord. Utilisez une bonne bibliothèque connue pour la pile de protocoles et le protocole de quorum plutôt que d'en faire un vous-même, sinon tout votre bon travail sur la redondance sera gaspillé lorsque le protocole de quorum se bloque.

  • Assurez-vous de la somme de contrôle (par exemple CRC / SHA) de votre FSM, et stockez le CRC / SHA dans le FSM lui-même (ainsi que la transmission dans le message, et la vérification des messages eux-mêmes). Obtenez les noeuds pour vérifier leur FSM régulièrement contre cette somme de contrôle, somme de contrôle des messages entrants, et vérifiez leur somme de contrôle correspond à la somme de contrôle du quorum.

  • Construisez autant de vérifications internes que possible sur votre système, en faisant redémarrer les nœuds qui détectent leur propre défaillance (cela vaut mieux que de continuer à travailler à moitié si vous avez suffisamment de nœuds). Essayez de les laisser se retirer proprement du quorum pendant le redémarrage au cas où ils ne reviendraient pas. Au redémarrage, donnez-leur la somme de contrôle de l'image logicielle (et toute autre chose qu'ils chargent) et faites un test de RAM complet avant de se réintroduire dans le quorum.

  • Utilisez le matériel pour vous soutenir, mais faites-le avec soin. Vous pouvez obtenir de la RAM ECC, par exemple, et la lire / écrire régulièrement pour corriger les erreurs ECC (et paniquer si l'erreur n'est pas corrigible). Cependant (de mémoire) la RAM statique est beaucoup plus tolérante aux rayonnements ionisants que la DRAM en premier lieu, donc mai mieux vaut utiliser une DRAM statique à la place. Voir aussi le premier point sous «choses que je ne ferais pas».

Supposons que vous ayez 1% de chances de défaillance d'un nœud donné en une journée, et prétendons que vous pouvez rendre les échecs totalement indépendants. Avec 5 nœuds, vous aurez besoin de trois pour échouer en un jour, ce qui représente une probabilité de .00001%. Avec plus, eh bien, vous avez l'idée.

Choses que je voudrais ne pas faire:

  • Sous-estimer la valeur de ne pas avoir le problème pour commencer. À moins que le poids ne soit une préoccupation, un gros bloc de métal autour de votre appareil va être une solution beaucoup moins chère et plus fiable qu'une équipe de programmeurs peut imaginer. Idem le couplage optique des entrées d'EMI est un problème, etc. Quoi qu'il en soit, tentez lors de l'approvisionnement de vos composants de trouver ceux qui sont les mieux notés contre les rayonnements ionisants.

  • Lancez vos propres algorithmes. Les gens ont déjà fait ça avant. Utilisez leur travail. La tolérance aux pannes et les algorithmes distribués sont difficiles. Utilisez le travail des autres si possible.

  • Utilisez des paramètres de compilation compliqués dans l'espoir naïf que vous détecter plus d'échecs. Si vous êtes chanceux, vous pouvez détecter plus d'échecs. Plus probablement, vous utiliserez un chemin de code dans le compilateur qui a été moins testé, en particulier si vous l'avez roulé vous-même.

  • Utilisez des techniques qui n'ont pas été testées dans votre environnement. La plupart des personnes qui écrivent des logiciels à haute disponibilité doivent simuler des modes de défaillance pour vérifier que leur HA fonctionne correctement, ce qui leur fait manquer de nombreux modes de défaillance. Vous êtes dans la position «chanceuse» d'avoir des échecs fréquents sur demande. Donc tester chaque technique, et s'assurer que son application réelle améliore le MTBF d'un montant qui dépasse la complexité de l'introduire (avec la complexité des bugs). Surtout appliquez cela à mes conseils sur les algorithmes de quorum, etc.


22
2018-04-27 15:41



Puisque vous demandez spécifiquement des solutions logicielles et que vous utilisez le C ++, pourquoi ne pas utiliser la surcharge de l'opérateur pour créer vos propres types de données sécurisés? Par exemple:

À la place d'utiliser uint32_t (et double, int64_t etc), faites le vôtre SAFE_uint32_t qui contient un multiple (minimum de 3) de uint32_t. Surcharge toutes les opérations que vous voulez (* + - / << >> = ==! = Etc) à effectuer, et effectuer les opérations surchargées de façon indépendante sur chaque valeur interne, c'est-à-dire ne pas le faire une fois et copier le résultat. Avant et après, vérifiez que toutes les valeurs internes correspondent. Si les valeurs ne correspondent pas, vous pouvez mettre à jour la mauvaise valeur avec la valeur la plus courante. S'il n'y a pas de valeur la plus commune, vous pouvez prévenir en toute sécurité qu'il y a une erreur.

De cette façon, peu importe si la corruption se produit dans l'ALU, dans les registres, dans la RAM ou sur un bus, vous aurez toujours plusieurs tentatives et vous aurez de très bonnes chances d'attraper des erreurs. Notez cependant que cela ne fonctionne que pour les variables que vous pouvez remplacer - votre pointeur de pile par exemple sera toujours sensible.

Une histoire parallèle: j'ai rencontré un problème similaire, également sur une ancienne puce ARM. Il s'est avéré être une chaîne d'outils qui utilisait une ancienne version de GCC qui, associée à la puce spécifique que nous utilisions, provoquait un bug dans certains cas de bords qui corrompaient (parfois) les valeurs passées dans les fonctions. Assurez-vous que votre appareil n'a aucun problème avant de le blâmer sur la radio-activité, et oui, parfois c'est un bug de compilateur =)


21
2018-04-27 15:32



Disclaimer: Je ne suis pas un professionnel de la radioactivité et je n'ai pas travaillé pour ce genre d'application. Mais j'ai travaillé sur les erreurs logicielles et la redondance pour l'archivage à long terme des données critiques, ce qui est quelque peu lié (même problème, objectifs différents).

À mon avis, le principal problème avec la radioactivité est que la radioactivité peut changer de la radioactivité peut altérer / altérer toute mémoire numérique. Ces erreurs sont généralement appelées erreurs souples, pourriture, etc.

La question est alors: comment calculer de façon fiable lorsque votre mémoire n'est pas fiable?

Pour réduire significativement le taux d'erreurs logicielles (au détriment de la surcharge informatique, car il s'agira principalement de solutions logicielles), vous pouvez:

  • compter sur le bon vieux plan de licenciement, et plus précisément le plus efficace codes de correction d'erreur (même but, mais des algorithmes plus intelligents pour que vous puissiez récupérer plus de bits avec moins de redondance). C'est parfois (à tort) aussi appelé checksumming. Avec ce type de solution, vous devrez stocker l'état complet de votre programme à tout moment dans une variable / classe maître (ou une structure?), Calculer un ECC et vérifier que l'ECC est correct avant de faire quoi que ce soit, et si non, réparez les champs. Cette solution ne garantit cependant pas que votre logiciel peut fonctionner (simplement qu'il fonctionnera correctement quand il le peut, ou il ne fonctionnera pas, car ECC peut vous dire si quelque chose ne va pas, et dans ce cas vous pouvez arrêter votre logiciel pour que vous n'obtenez pas de faux résultats).

  • ou vous pouvez utiliser structures de données algorithmiques résilientes, ce qui garantit, jusqu'à un certain point, que votre programme donnera toujours des résultats corrects même en présence d'erreurs logicielles. Ces algorithmes peuvent être vus comme un mélange de structures algorithmiques communes avec des schémas ECC mélangés nativement, mais cela est beaucoup plus résilient que cela, parce que le schéma de résilience est étroitement lié à la structure, de sorte que vous n'avez pas besoin d'encoder des procédures supplémentaires pour vérifier l'ECC, et ils sont généralement beaucoup plus rapides. Ces structures fournissent un moyen de s'assurer que votre programme fonctionnera dans n'importe quelle condition, jusqu'à la limite théorique des erreurs logicielles. Vous pouvez également mélanger ces structures résilientes avec le schéma de redondance / ECC pour plus de sécurité (ou coder vos structures de données les plus importantes comme résilientes, et le reste, les données non récupérables que vous pouvez recalculer à partir des structures de données principales). un peu d'ECC ou un contrôle de parité qui est très rapide à calculer).

Si vous êtes intéressé par les structures de données résilientes (ce qui est un domaine récent, mais passionnant, en algorithmique et en ingénierie de redondance), je vous conseille de lire les documents suivants:

  • Introduction aux structures de données d'algorithmes résilients par Giuseppe F. Italiano, Universita di Roma "Tor Vergata"

  • Christiano, P., Demaine, E.D., & Kishore, S. (2011). Structures de données tolérantes aux pannes sans perte avec surcharge additive. Dans Algorithmes et structures de données (pp. 243-254). Springer Berlin Heidelberg.

  • Ferraro-Petrillo, U., Grandoni, F. et Italiano, G. F. (2013). Structures de données résilientes aux fautes de mémoire: une étude expérimentale des dictionnaires. Journal of Expérimental Algorithmics (JEA), 18, 1-6.

  • Italiano, G. F. (2010). Algorithmes résilients et structures de données. Dans Algorithmes et complexité (pp. 13-24). Springer Berlin Heidelberg.

Si vous souhaitez en savoir plus sur le domaine des structures de données résilientes, vous pouvez consulter les travaux de Giuseppe F. Italiano (et travaillez votre chemin à travers les refs) et le Modèle de RAM défectueux (présenté dans Finocchi et al., 2005, Finocchi et Italiano, 2008).

/ EDIT: J'ai illustré la prévention / récupération des erreurs logicielles principalement pour la mémoire RAM et le stockage de données, mais je n'ai pas parlé de erreurs de calcul (CPU). D'autres réponses pointaient déjà vers l'utilisation de transactions atomiques comme dans les bases de données, donc je proposerai un autre schéma plus simple: redondance et vote majoritaire.

L'idée est que vous simplement faire x fois le même calcul pour chaque calcul que vous devez faire, et stocker le résultat dans x variables différentes (avec x> = 3). Vous pouvez ensuite comparez vos variables x:

  • s'ils sont tous d'accord, il n'y a pas du tout d'erreur de calcul.
  • s'ils sont en désaccord, vous pouvez utiliser un vote majoritaire pour obtenir la valeur correcte, et puisque cela signifie que le calcul a été partiellement corrompu, vous pouvez également déclencher une analyse du système / état du programme pour vérifier que le reste est correct.
  • Si le vote majoritaire ne peut pas déterminer un gagnant (toutes les valeurs x sont différentes), alors c'est un signal parfait pour déclencher la procédure de sécurité intégrée (redémarrer, envoyer une alerte à l'utilisateur, etc.).

Ce système de licenciement est très vite comparé à ECC (pratiquement O (1)) et il vous fournit un signal clair quand tu as besoin de sécurité intégrée. Le vote majoritaire est aussi (presque) garanti pour ne jamais produire de sortie corrompue et aussi récupérer des erreurs de calcul mineures, parce que la probabilité que les calculs x donnent la même sortie est infinitésimale (parce qu'il y a énormément de sorties possibles, il est presque impossible d'obtenir aléatoirement 3 fois la même chose, encore moins de chances si x> 3).

Donc, avec un vote majoritaire, vous êtes à l'abri d'une sortie corrompue, et avec redondance x == 3, vous pouvez récupérer 1 erreur (avec x == 4, il y aura 2 erreurs récupérables, etc. - l'équation exacte est nb_error_recoverable == (x-2) où x est le nombre de répétitions de calculs car il faut au moins 2 calculs d'accord pour récupérer en utilisant le vote majoritaire).

L'inconvénient est que vous devez calculer x fois au lieu d'une fois, de sorte que vous avez un coût de calcul supplémentaire, mais la complexité linéaire est si asymptotiquement vous ne perdez pas beaucoup pour les avantages que vous gagnez. Un moyen rapide de faire un vote majoritaire est de calculer le mode sur un tableau, mais vous pouvez aussi utiliser un filtre médian.

De plus, si vous voulez vous assurer que les calculs sont effectués correctement, vous pouvez construire votre propre matériel avec x processeurs, et câbler le système afin que les calculs soient automatiquement dupliqués sur les processeurs x avec un vote majoritaire. mécaniquement à la fin (en utilisant des portes ET / OU par exemple). Ceci est souvent mis en œuvre dans les avions et les dispositifs critiques (voir redondance modulaire triple). De cette façon, vous n'aurez pas de surcharge de calcul (puisque les calculs supplémentaires seront effectués en parallèle), et vous aurez une autre couche de protection contre les erreurs logicielles (puisque la duplication de calcul et le vote majoritaire seront gérés directement par le matériel et non par logiciel - qui peut plus facilement être corrompu car un programme est simplement des bits stockés en mémoire ...).


16
2018-05-01 18:56