Compilando um aplicativo para uso em ambientes altamente radioativos


1456

Estamos compilando um aplicativo C / C ++ incorporado que é implantado em um dispositivo protegido em um ambiente bombardeado com radiação ionizante . Estamos usando o GCC e a compilação cruzada para o ARM. Quando implantado, nosso aplicativo gera alguns dados incorretos e trava com mais frequência do que gostaríamos. O hardware foi projetado para esse ambiente e nosso aplicativo é executado nesta plataforma há vários anos.

Existem alterações que podemos fazer em nosso código ou melhorias em tempo de compilação que podem ser feitas para identificar / corrigir erros de software e corrupção de memória causados ​​por distúrbios de um único evento ? Algum outro desenvolvedor teve sucesso em reduzir os efeitos nocivos de erros de software em um aplicativo de longa duração?


186
Os valores na memória estão mudando ou no processador? Se o hardware foi projetado para o ambiente, o software deve ser executado como se estivesse sendo executado em um ambiente não radioativo.
21716 Thomas

3
Se possível, você deve configurar um sistema de registro que armazene eventos na memória não volátil e resistente à radiação. Armazene informações suficientes para poder rastrear o evento e encontrar facilmente a causa raiz.
Thomas Matthews

2
Thomas Thomass Toda a memória tem uma taxa de erro FIT, e os fabricantes de hardware fazem muitas promessas. A maioria dos problemas provavelmente é causada pela modificação dos RAMs no SEU em tempo de execução.
rook

9
Essa é uma solução combinada de hardware / software, mas eu sei que a Texas Instruments (e provavelmente outras) fabrica chips incorporados para aplicativos críticos de segurança que consistem em dois núcleos duplicados, rodando em fechadura, meio ciclo de relógio fora de fase. Existem interrupções especiais e ações de redefinição que são executadas quando o hardware detecta algo diferente entre os núcleos, para que você possa se recuperar de erros. Acredito que a TI os classifique como processadores de segurança "Hercules".
mbrig

5
Motores robustos redundantes, algumas engrenagens, eixos e catracas! Substitua anualmente ou mais frequentemente conforme as taxas de dose exigirem. Não, sério, minha primeira pergunta com esse tipo de problema sempre foi: você realmente precisa de tanto software? Seja o mais analógico possível.
jwdonahue

Respostas:


814

Trabalhando por cerca de 4-5 anos no desenvolvimento de software / firmware e testes ambientais de satélites miniaturizados *, gostaria de compartilhar minha experiência aqui.

* (os satélites miniaturizados são muito mais propensos a perturbações em eventos únicos do que os satélites maiores, devido aos seus tamanhos relativamente pequenos e limitados para seus componentes eletrônicos )

Para ser muito conciso e direto: não há mecanismo para se recuperar de uma situação detectável e errônea pelo próprio software / firmware sem , pelo menos, uma cópia da versão mínima de trabalho do software / firmware em algum lugar para fins de recuperação - e com o suporte de hardware a recuperação (funcional).

Agora, essa situação é normalmente tratada no nível de hardware e software. Aqui, como você pede, vou compartilhar o que podemos fazer no nível do software.

  1. ... finalidade de recuperação . Forneça a capacidade de atualizar / recompilar / atualizar novamente seu software / firmware em ambiente real. Esse é um recurso quase obrigatório para qualquer software / firmware em ambiente altamente ionizado. Sem isso, você pode ter software / hardware redundante quantos quiser, mas a certa altura, todos eles vão explodir. Então, prepare esse recurso!

  2. ... versão mínima de trabalho ... Tenha em seu código cópias responsivas e múltiplas, versão mínima do software / firmware. É como o modo de segurança no Windows. Em vez de ter apenas uma versão totalmente funcional do seu software, tenha várias cópias da versão mínima do seu software / firmware. A cópia mínima geralmente terá muito menos tamanho que a cópia completa e quase sempre terá apenas os dois ou três recursos a seguir:

    1. capaz de ouvir comandos do sistema externo,
    2. capaz de atualizar o software / firmware atual,
    3. capaz de monitorar os dados de manutenção da operação básica.
  3. ... copiar ... em algum lugar ... Tem software / firmware redundante em algum lugar.

    1. Você poderia, com ou sem hardware redundante, tentar ter software / firmware redundante no seu ARM uC. Isso normalmente é feito com dois ou mais softwares / firmware idênticos em endereços separados, que enviam pulsação um para o outro - mas apenas um estará ativo por vez. Se um ou mais software / firmware não responder, mude para o outro software / firmware. O benefício de usar essa abordagem é que podemos ter substituição funcional imediatamente após a ocorrência de um erro - sem nenhum contato com qualquer sistema / parte externa responsável por detectar e reparar o erro (no caso de satélite, geralmente é o Mission Control Center ( MCC)).

      Estritamente falando, sem hardware redundante, a desvantagem de fazer isso é que você realmente não pode eliminar todos os pontos únicos de falhas. No mínimo, você ainda terá um único ponto de falha, que é o próprio switch (ou geralmente o início do código). No entanto, para um dispositivo limitado pelo tamanho em um ambiente altamente ionizado (como satélites pico / femto), a redução do ponto único de falha para um ponto ainda vale a pena considerar sem hardware adicional. Em algum momento, o trecho de código para a troca certamente seria muito menor que o código para todo o programa - reduzindo significativamente o risco de obter um Evento Único nele.

    2. Mas se você não estiver fazendo isso, deverá ter pelo menos uma cópia em seu sistema externo que possa entrar em contato com o dispositivo e atualizar o software / firmware (no caso do satélite, é novamente o centro de controle da missão).

    3. Você também pode ter a cópia em seu armazenamento de memória permanente no dispositivo, que pode ser acionada para restaurar o software / firmware do sistema em execução
  4. ... situação incorreta detectável. O erro deve ser detectável , geralmente pelo circuito de correção / detecção de erros de hardware ou por um pequeno pedaço de código para correção / detecção de erros. É melhor colocar esse código pequeno, múltiplo e independente do software / firmware principal. Sua principal tarefa é apenas para verificar / corrigir. Se o circuito / firmware do hardware for confiável(como é mais resistente à radiação do que os demais - ou possui vários circuitos / lógicas), considere fazer a correção de erros com ela. Mas se não for, é melhor fazê-lo como detecção de erro. A correção pode ser por sistema / dispositivo externo. Para a correção de erros, você pode considerar utilizar um algoritmo básico de correção de erros, como Hamming / Golay23, porque eles podem ser implementados com mais facilidade, tanto no circuito / software. Mas, em última análise, depende da capacidade da sua equipe. Para detecção de erros, normalmente o CRC é usado.

  5. ... hardware de suporte à recuperação Agora, trata-se do aspecto mais difícil sobre esse assunto. Por fim, a recuperação exige que o hardware responsável pela recuperação seja pelo menos funcional. Se o hardware estiver permanentemente quebrado (normalmente ocorre após a dose ionizante total atingir determinado nível), então (infelizmente) não há como o software ajudar na recuperação. Assim, o hardware é justamente a preocupação de maior importância para um dispositivo exposto a um alto nível de radiação (como satélite).

Além da sugestão de antecipar o erro de firmware acima devido à perturbação de evento único, também gostaria de sugerir que você tenha:

  1. Algoritmo de detecção e / ou correção de erros no protocolo de comunicação entre subsistemas. Este é outro quase necessário para evitar sinais incompletos / errados recebidos de outro sistema

  2. Filtre sua leitura do ADC. Você não usar o ADC leitura diretamente. Filtre-o por filtro mediano, filtro médio ou qualquer outro filtro - nunca confie no valor de leitura única. Prove mais, não menos - razoavelmente.


401

A NASA tem um artigo sobre software reforçado por radiação . Descreve três tarefas principais:

  1. Monitoramento regular da memória quanto a erros e eliminação desses erros,
  2. mecanismos robustos de recuperação de erros e
  3. a capacidade de reconfigurar se algo não funcionar mais.

Observe que a taxa de varredura de memória deve ser frequente o suficiente para que erros de vários bits raramente ocorram, pois a maioria da memória ECC pode se recuperar de erros de um bit, e não de vários bits.

A recuperação robusta de erros inclui transferência de fluxo de controle (normalmente reiniciando um processo em um ponto anterior ao erro), liberação de recursos e restauração de dados.

Sua principal recomendação para restauração de dados é evitar a necessidade, pois os dados intermediários são tratados como temporários, para que a reinicialização antes do erro também reverta os dados para um estado confiável. Isso soa semelhante ao conceito de "transações" nos bancos de dados.

Eles discutem técnicas particularmente adequadas para linguagens orientadas a objetos, como C ++. Por exemplo

  1. ECCs baseados em software para objetos de memória contíguos
  2. Programação por contrato : verificação de pré-condições e pós-condições e, em seguida, verificando o objeto para verificar se ele ainda está em um estado válido.

E, por acaso, a NASA usou C ++ para grandes projetos, como o Mars Rover .

A abstração e o encapsulamento da classe C ++ possibilitaram o rápido desenvolvimento e teste entre vários projetos e desenvolvedores.

Eles evitaram certos recursos do C ++ que poderiam criar problemas:

  1. Exceções
  2. Modelos
  3. Iostream (sem console)
  4. Herança múltipla
  5. Sobrecarga do operador (exceto newe delete)
  6. Alocação dinâmica (usou um pool de memória dedicado e posicionamento newpara evitar a possibilidade de corrupção de heap do sistema).

28
Isso realmente soa como algo em que uma linguagem pura seria boa. Como os valores nunca mudam, se eles forem danificados, você pode voltar à definição original (que é como deveria ser) e acidentalmente não fará a mesma coisa duas vezes (devido à falta de efeitos colaterais).
PyRulez

20
RAII é uma péssima idéia, porque você não pode depender de seu desempenho correto ou mesmo de todo. Isso pode danificar seus dados aleatoriamente etc. Você realmente deseja o máximo de imutabilidade possível, além de mecanismos de correção de erros. É muito mais fácil jogar fora as coisas quebradas do que tentar repará-las de alguma forma (como exatamente você sabe o suficiente para voltar ao estado antigo correto?). Você provavelmente deseja usar uma linguagem bastante estúpida para isso - as otimizações podem prejudicar mais do que ajudam.
Luaan

67
@ PyRulez: Linguagens puras são uma abstração, hardware não é puro. Compiladores são muito bons em esconder a diferença. Se o seu programa tiver um valor que logicamente não deve mais ser usado após a etapa X, o compilador poderá substituí-lo por um valor calculado na etapa X + 1. Mas isso significa que você não pode voltar. Mais formalmente, os possíveis estados de um programa em linguagem pura formam um gráfico acíclico, o que significa que dois estados são equivalentes e podem ser mesclados quando os estados alcançáveis ​​de ambos são equivalentes. Essa fusão destrói a diferença de caminhos que levam a esses estados.
MSalters

2
@Vorac - De acordo com a apresentação, a preocupação com os modelos C ++ é o inchaço do código.
jww 12/06/19

3
@DeerSpotter O problema exato é muito maior do que isso. A ionização pode danificar bits do seu programa watcher em execução. Então você vai precisar de um observador de um observador, então - observador de um observador de um observador e assim por diante ...
Agnius Vasiliauskas

116

Aqui estão alguns pensamentos e idéias:

Use a ROM de maneira mais criativa.

Armazene o que puder na ROM. Em vez de calcular coisas, armazene tabelas de consulta na ROM. (Verifique se o compilador está exibindo suas tabelas de consulta na seção somente leitura! Imprima os endereços de memória em tempo de execução para verificar!) Armazene sua tabela de vetor de interrupção na ROM. Obviamente, execute alguns testes para ver o quão confiável sua ROM é comparada à sua RAM.

Use sua melhor RAM para a pilha.

Os SEUs na pilha são provavelmente a fonte mais provável de falhas, porque é onde coisas como variáveis ​​de índice, variáveis ​​de status, endereços de retorno e ponteiros de vários tipos normalmente vivem.

Implemente rotinas de timer-tick e watchdog.

Você pode executar uma rotina de "verificação de integridade" a cada marca de timer, bem como uma rotina de vigilância para lidar com o bloqueio do sistema. Seu código principal também pode incrementar periodicamente um contador para indicar progresso, e a rotina de verificação de integridade pode garantir que isso ocorra.

Implemente códigos de correção de erros no software.

Você pode adicionar redundância aos seus dados para poder detectar e / ou corrigir erros. Isso aumentará o tempo de processamento, potencialmente deixando o processador exposto à radiação por mais tempo, aumentando assim a chance de erros; portanto, você deve considerar o trade-off.

Lembre-se dos caches.

Verifique os tamanhos dos caches da sua CPU. Os dados que você acessou ou modificou recentemente provavelmente estarão em um cache. Eu acredito que você pode desativar pelo menos alguns dos caches (com um grande custo de desempenho); você deve tentar isso para ver como os caches são suscetíveis aos SEUs. Se os caches forem mais difíceis que a RAM, você poderá ler e reescrever regularmente dados críticos para garantir que eles permaneçam no cache e traga a RAM de volta à linha.

Use manipuladores de falhas de página de maneira inteligente.

Se você marcar uma página de memória como não presente, a CPU emitirá uma falha de página quando você tentar acessá-la. Você pode criar um manipulador de falhas de página que faça alguma verificação antes de atender à solicitação de leitura. (Os sistemas operacionais de PC usam isso para carregar de forma transparente as páginas que foram trocadas para o disco.)

Use a linguagem assembly para coisas críticas (que podem ser tudo).

Com a linguagem assembly, você sabe o que há nos registros e o que há na RAM; você sabe quais tabelas de RAM especiais a CPU está usando e pode projetar coisas de maneira indireta para manter seu risco baixo.

Usar objdump para realmente examinar a linguagem assembly gerada e descobrir quanto código cada uma de suas rotinas ocupa.

Se você estiver usando um grande sistema operacional como o Linux, estará pedindo problemas; há tanta complexidade e tantas coisas para dar errado.

Lembre-se de que é um jogo de probabilidades.

Um comentarista disse

Toda rotina que você escreve para detectar erros estará sujeita à falha da mesma causa.

Embora isso seja verdade, as chances de erros nos (digamos) 100 bytes de código e dados necessários para que uma rotina de verificação funcione corretamente são muito menores do que as chances de erros em outros lugares. Se sua ROM é bastante confiável e quase todo o código / dados está realmente na ROM, então suas chances são ainda melhores.

Use hardware redundante.

Use 2 ou mais configurações de hardware idênticas com código idêntico. Se os resultados diferirem, uma redefinição deve ser acionada. Com 3 ou mais dispositivos, você pode usar um sistema de "votação" para tentar identificar qual deles foi comprometido.


14
Atualmente, existe um ECC disponível por hardware, o que economiza o tempo de processamento. O primeiro passo seria escolher um microcontrolador com ECC embutido.
Lundin

23
Em algum lugar no fundo da minha mente, há uma referência ao hardware de aviônicos (talvez ônibus espaciais?) Em que a arquitetura redundante foi explicitamente projetada para não ser idêntica (e por equipes diferentes). Isso reduz a possibilidade de um erro sistêmico no design do hardware / software, reduzindo a possibilidade de todos os sistemas de votação travarem ao mesmo tempo quando confrontados com as mesmas entradas.
Peter M

8
@PeterM: AFAIK, também reivindicado pelo software de voo do Boeing 777: três versões por três equipes em três linguagens de programação.
Restabeleça Monica - M. Schröder

7
A @DanEsparza RAM normalmente possui um capacitor (DRAM) ou alguns transistores no feedback (SRAM) que armazenam dados. Um evento de radiação pode carregar / descarregar espuriosamente o capacitor ou alterar o sinal no loop de feedback. A ROM normalmente não precisa ser escrita (pelo menos sem circunstâncias especiais e / ou tensões mais altas) e, portanto, pode ser inerentemente mais estável no nível físico.
Nanofarad

7
@ DanEsparza: Existem vários tipos de memórias ROM. Se a "ROM" for emulada por, por exemplo, eeprom ou flash somente de leitura em 5v, mas programável em 10v, então, na verdade, essa "ROM" ainda é propensa a ionização. Talvez um pouco menos que os outros. No entanto, existem coisas boas e antigas como Mask ROM ou PROM baseado em fusível que eu acho que precisariam de uma quantidade realmente séria de radiação para começar a falhar. Não sei, no entanto, se ainda são fabricados.
quetzalcoatl

105

Você também pode estar interessado na rica literatura sobre o assunto da tolerância a falhas algorítmicas. Isso inclui a atribuição antiga: escreva uma classificação que classifique corretamente sua entrada quando um número constante de comparações falhará (ou, a versão um pouco mais ruim, quando o número assintótico de comparações com falha for escalonado como log(n)nas ncomparações).

Um lugar para começar a ler é o artigo de Huang e Abraham, de 1984, " Tolerância a falhas baseada em algoritmos para operações matriciais ". A ideia deles é vagamente semelhante à computação criptografada homomórfica (mas não é a mesma, pois eles estão tentando detectar / corrigir erros no nível da operação).

Um descendente mais recente desse artigo é Bosilca, Delmas, Dongarra e " tolerância a falhas baseada em algoritmo aplicada à computação de alto desempenho ".


5
Eu realmente gosto da sua resposta. Essa é uma abordagem de software mais genérica para a integridade dos dados e uma solução de tolerância a falhas baseada em algoritmos será usada em nosso produto final. Obrigado!
rook

41

Escrever código para ambientes radioativos não é realmente diferente de escrever código para qualquer aplicativo de missão crítica.

Além do que já foi mencionado, aqui estão algumas dicas diversas:

  • Use as medidas diárias de segurança "pão e manteiga" que devem estar presentes em qualquer sistema embarcado semi-profissional: vigilância interna, detecção interna de baixa tensão, monitor interno de relógio. Essas coisas nem precisam ser mencionadas no ano de 2016 e são padrão em praticamente todos os microcontroladores modernos.
  • Se você tiver um MCU orientado para a segurança e / ou automotivo, ele terá certos recursos de watchdog, como uma janela de tempo especificada, dentro da qual você precisará atualizar o watchdog. É preferível se você tiver um sistema em tempo real de missão crítica.
  • Em geral, use um MCU adequado para esse tipo de sistema, e não alguns fluff genéricos que você recebeu em um pacote de flocos de milho. Hoje em dia, quase todos os fabricantes de MCUs possuem MCUs especializados projetados para aplicações de segurança (TI, Freescale, Renesas, ST, Infineon etc.). Eles possuem muitos recursos de segurança internos, incluindo núcleos de etapa de bloqueio: o que significa que existem 2 núcleos de CPU executando o mesmo código e devem concordar entre si.
  • IMPORTANTE: Você deve garantir a integridade dos registros internos do MCU. Todos os registros de controle e status dos periféricos de hardware graváveis ​​podem estar localizados na memória RAM e, portanto, são vulneráveis.

    Para se proteger contra a corrupção de registros, escolha um microcontrolador com recursos incorporados de "gravação única" dos registros. Além disso, você precisa armazenar valores padrão de todos os registros de hardware no NVM e copiar esses valores para seus registros em intervalos regulares. Você pode garantir a integridade de variáveis ​​importantes da mesma maneira.

    Nota: sempre use programação defensiva. Isso significa que você precisa configurar todos os registros no MCU e não apenas os usados ​​pelo aplicativo. Você não quer que algum periférico aleatório de hardware acorde de repente.

  • Existem todos os tipos de métodos para verificar se há erros na RAM ou NVM: somas de verificação, "padrões de caminhada", ECC de software etc. etc. Atualmente, a melhor solução é não usar nenhum deles, mas usar um MCU com ECC embutido e verificações semelhantes. Como fazer isso no software é complexo, a verificação de erros por si só pode, portanto, introduzir bugs e problemas inesperados.

  • Use redundância. Você pode armazenar memória volátil e não volátil em dois segmentos "espelhados" idênticos, que sempre devem ser equivalentes. Cada segmento pode ter uma soma de verificação CRC anexada.
  • Evite usar memórias externas fora do MCU.
  • Implemente uma rotina de serviço de interrupção padrão / manipulador de exceção padrão para todas as possíveis interrupções / exceções. Mesmo os que você não está usando. A rotina padrão não deve fazer nada, exceto desligar sua própria fonte de interrupção.
  • Compreender e adotar o conceito de programação defensiva. Isso significa que seu programa precisa lidar com todos os casos possíveis, mesmo aqueles que não podem ocorrer na teoria. Exemplos .

    O firmware de missão crítica de alta qualidade detecta o máximo de erros possível e os ignora de maneira segura.

  • Nunca escreva programas que dependam de comportamento mal especificado. É provável que esse comportamento possa mudar drasticamente com alterações inesperadas do hardware causadas por radiação ou EMI. A melhor maneira de garantir que seu programa esteja livre dessa porcaria é usar um padrão de codificação como o MISRA, junto com uma ferramenta de análise estática. Isso também ajudará na programação defensiva e na eliminação de erros (por que você não deseja detectar erros em qualquer tipo de aplicativo?).
  • IMPORTANTE: Não implemente confiança nos valores padrão das variáveis ​​de duração do armazenamento estático. Ou seja, não confie no conteúdo padrão do .dataou.bss . Pode haver qualquer quantidade de tempo entre o ponto de inicialização e o ponto em que a variável é realmente usada; pode haver bastante tempo para a RAM ser corrompida. Em vez disso, escreva o programa para que todas essas variáveis ​​sejam definidas no NVM em tempo de execução, imediatamente antes do momento em que essa variável for usada pela primeira vez.

    Na prática, isso significa que se uma variável for declarada no escopo do arquivo ou como static, você nunca deve usar= -la para inicializá-la (ou pode, mas é inútil, porque você não pode confiar no valor de qualquer maneira). Sempre defina-o em tempo de execução, imediatamente antes do uso. Se for possível atualizar repetidamente essas variáveis ​​do NVM, faça-o.

    Da mesma forma em C ++, não confie em construtores para variáveis ​​de duração de armazenamento estático. Peça ao construtor que chame uma rotina pública de "configuração", que você também pode chamar mais tarde em tempo de execução, diretamente do aplicativo de chamada.

    Se possível, remova o código de inicialização "copiar" que inicializa .datae .bss(e chama os construtores C ++) completamente, para que você obtenha erros de vinculador se escrever código contando com isso. Muitos compiladores têm a opção de pular isso, geralmente chamado de "inicialização mínima / rápida" ou similar.

    Isso significa que todas as bibliotecas externas precisam ser verificadas para que não contenham essa confiança.

  • Implemente e defina um estado seguro para o programa, para onde você reverterá em caso de erros críticos.

  • A implementação de um sistema de relatório de erros / log de erros é sempre útil.

Uma maneira de lidar com os booleanos que estão sendo corrompidos (como no link de exemplo) pode ser tornar TRUEigual para 0xffffffffdepois usar POPCNTcom um limite.
wizzwizz4

@ wizzwizz4 Dado que o valor 0xff é o valor padrão da célula flash não programada, isso parece uma má ideia.
Lundin

%01010101010101010101010101010101, XOR então POPCNT?
Wizzwizz4

1
@ wizzwizz4 Ou apenas o valor 0x1, conforme exigido pelo padrão C.
Lundin

1
@ wizzwizz4 Por que você usa alguns ou todos os métodos mencionados acima (ECC, CRC etc). Caso contrário, o raio cósmico também pode virar um único bit na sua .textseção, alterando um código operacional ou similar.
Lundin

34

Pode ser possível usar C para escrever programas que se comportam de maneira robusta nesses ambientes, mas apenas se a maioria das formas de otimização do compilador estiver desabilitada. Os compiladores de otimização foram projetados para substituir muitos padrões de codificação aparentemente redundantes por "mais eficientes" e podem não ter idéia de que o motivo pelo qual o programador está testando x==42quando o compilador sabe que não há como xter mais alguma coisa, porque o programador deseja impedir a execução de determinado código xmantendo outro valor - mesmo nos casos em que a única maneira de manter esse valor seria se o sistema recebesse algum tipo de falha elétrica.

Declarar variáveis ​​como volatilemuitas vezes é útil, mas pode não ser uma panacéia. De particular importância, observe que a codificação segura geralmente requer que operações perigosas tenham intertravamentos de hardware que exigem várias etapas para serem ativados e que o código seja escrito usando o padrão:

... 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();

Se um compilador converter o código de maneira relativamente literal e se todas as verificações do estado do sistema forem repetidas após o mesmo prepare_for_activation(), o sistema poderá ser robusto contra praticamente qualquer evento de falha única plausível, mesmo aqueles que corromperiam arbitrariamente o contador e a pilha do programa. Se ocorrer uma falha logo após uma chamada prepare_for_activation(), isso implicaria que a ativação seria apropriada (já que não há outro motivo prepare_for_activation()que teria sido chamado antes da falha). Se a falha fizer com que o código chegue de forma prepare_for_activation()inadequada, mas não houver eventos subsequentes de falha, não haveria maneira de o código chegar posteriormente trigger_activation()sem passar pela verificação de validação ou chamando cancel_preparations primeiro [se a pilha falha, a execução pode prosseguir para um local logo antestrigger_activation()após o contexto que chamou prepare_for_activation()retornos, mas a chamada para cancel_preparations()teria ocorrido entre as chamadas para prepare_for_activation()e trigger_activation(), tornando a última chamada inofensiva.

Esse código pode ser seguro no C tradicional, mas não nos compiladores C modernos. Esses compiladores podem ser muito perigosos nesse tipo de ambiente, porque eles se esforçam para incluir apenas códigos que serão relevantes em situações que poderiam ocorrer por meio de algum mecanismo bem definido e cujas conseqüências resultantes também seriam bem definidas. Código cujo objetivo seria detectar e limpar após falhas pode, em alguns casos, acabar piorando as coisas. Se o compilador determinar que a tentativa de recuperação, em alguns casos, invocará um comportamento indefinido, poderá inferir que as condições que exigiriam essa recuperação nesses casos não podem ocorrer, eliminando o código que seria verificado por eles.


6
Realisticamente falando, quantos compiladores modernos existem que não oferecem -O0ou um switch equivalente? O GCC fará muitas coisas estranhas se você der permissão , mas se você pedir para não fazê-lo, também poderá ser bastante literal.
precisa saber é o seguinte

24
Desculpe, mas essa ideia é fundamentalmente perigosa. Desativar otimizações produz um programa mais lento. Ou, em outras palavras, você precisa de uma CPU mais rápida. Por acaso, as CPUs mais rápidas são mais rápidas porque as cargas nos portões dos transistores são menores. Isso os torna muito mais suscetíveis à radiação. A melhor estratégia é usar um chip grande e lento, onde um único fóton tem muito menos probabilidade de derrubar um pouco e recuperar a velocidade -O2.
precisa saber é o seguinte

27
Uma razão secundária por que -O0é uma má ideia é porque emite muito mais instruções inúteis. Exemplo: uma chamada não embutida contém instruções para salvar registros, fazer a chamada, restaurar registros. Tudo isso pode falhar. Uma instrução que não está lá não pode falhar.
MSalters

15
Outro motivo -O0é uma má idéia: ela tende a armazenar variáveis ​​na memória em vez de em um registro. Agora não é certo que a memória seja mais suscetível aos SEU, mas os dados em voo são mais suscetíveis que os dados em repouso. A movimentação inútil de dados deve ser evitada e -O2ajuda lá.
precisa saber é o seguinte

9
@MSalters: O importante não é que os dados não sejam imunes a interrupções, mas que o sistema possa lidar com interrupções de maneira a atender aos requisitos. Em muitos compiladores, a desativação de todas as otimizações gera um código que executa um número excessivo de movimentos de registro para registro, o que é ruim, mas o armazenamento de variáveis ​​na memória é mais seguro do ponto de vista da recuperação do que mantê-las nos registros. Se alguém tem duas variáveis ​​na memória que devem obedecer a alguma condição (por exemplo, v1=v2+0xCAFEBABEe todas as atualizações para as duas variáveis ​​são feitas ... #
687

29

Este é um assunto extremamente amplo. Basicamente, você não pode realmente se recuperar da corrupção de memória, mas pode pelo menos tentar falhar imediatamente . Aqui estão algumas técnicas que você pode usar:

  • dados constantes de soma de verificação . Se você tiver quaisquer dados de configuração que permaneçam constantes por um longo período de tempo (incluindo os registros de hardware configurados), calcule sua soma de verificação na inicialização e verifique-a periodicamente. Quando você vê uma incompatibilidade, é hora de reinicializar ou redefinir.

  • armazene variáveis ​​com redundância . Se você tem uma variável importante x, escrever o seu valor em x1, x2e x3e lê-lo como (x1 == x2) ? x2 : x3.

  • implementar o monitoramento do fluxo do programa . XOR um sinalizador global com um valor exclusivo em funções / ramificações importantes chamadas do loop principal. A execução do programa em um ambiente livre de radiação com cobertura de teste de quase 100% deve fornecer a lista de valores aceitáveis ​​da bandeira no final do ciclo. Redefina se você encontrar desvios.

  • monitorar o ponteiro da pilha . No início do loop principal, compare o ponteiro da pilha com o valor esperado. Redefinir no desvio.


27

O que poderia ajudá-lo é um cão de guarda . Os cães de guarda foram usados ​​extensivamente na computação industrial nos anos 80. As falhas de hardware eram muito mais comuns na época - outra resposta também se refere a esse período.

Um cão de guarda é um recurso combinado de hardware / software. O hardware é um contador simples que diminui de um número (digamos 1023) para zero. TTL ou outra lógica pode ser usada.

O software foi projetado para que uma rotina monitore a operação correta de todos os sistemas essenciais. Se essa rotina for concluída corretamente = encontrar o computador funcionando corretamente, o contador voltará para 1023.

O design geral é para que, em circunstâncias normais, o software impeça que o contador de hardware atinja zero. Caso o contador atinja zero, o hardware do contador executa sua tarefa única e redefine o sistema inteiro. De uma perspectiva de contador, zero é igual a 1024 e o contador continua em contagem regressiva novamente.

Esse watchdog garante que o computador conectado seja reiniciado em muitos casos de falha. Devo admitir que não estou familiarizado com hardware capaz de executar essa função nos computadores de hoje. As interfaces para o hardware externo agora são muito mais complexas do que costumavam ser.

Uma desvantagem inerente do watchdog é que o sistema não está disponível desde o momento em que falha até que o contador do watchdog atinja zero + tempo de reinicialização. Embora esse tempo seja geralmente muito menor do que qualquer intervenção externa ou humana, o equipamento suportado precisará poder continuar sem o controle do computador durante esse período.


9
Os vigilantes contrários binários com CIs padrão TTL são realmente uma solução dos anos 80. Não faça isso. Hoje, não existe um único MCU no mercado sem o circuito interno de vigilância. Tudo o que você precisa verificar é se o cão de guarda interno possui uma fonte de relógio individual (boa, provavelmente o caso) ou se herda o relógio do relógio do sistema (ruim).
Lundin

1
Ou implementar o cão de guarda em um FPGA: ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20130013486.pdf
nºs

2
Ainda usado extensivamente em processadores embarcados, aliás.
Graham

5
@ Peter Mortensen Por favor, interrompa sua edição em todas as respostas a esta pergunta. Isso não é Wikipedia, e esses links não são úteis (e tenho certeza que todo mundo sabe como encontrar a Wikipedia de qualquer maneira ...). Muitas de suas edições estão incorretas porque você não conhece o tópico. Estou revertendo suas edições incorretas à medida que as encontro. Você não está melhorando este tópico, mas pior. Pare de editar.
Lundin

Jack Ganssle tem um bom artigo sobre cães de guarda: ganssle.com/watchdogs.htm
Igor Skochinsky 29/04

23

Esta resposta pressupõe que você esteja preocupado em ter um sistema que funcione corretamente, além de ter um sistema de custo mínimo ou rápido; a maioria das pessoas brincando com coisas radioativas valoriza a correção / a segurança sobre a velocidade / custo

Várias pessoas sugeriram mudanças de hardware que você pode fazer (ótimo - já há muitas coisas boas aqui nas respostas e eu não pretendo repetir tudo), e outras sugeriram redundância (ótima em princípio), mas acho que não alguém sugeriu como essa redundância pode funcionar na prática. Como você falha? Como você sabe quando algo "deu errado"? Muitas tecnologias funcionam com base em que tudo funcionará, e o fracasso é, portanto, uma coisa complicada de se lidar. No entanto, algumas tecnologias de computação distribuída projetadas para escala esperam falhas (afinal, com escala suficiente, a falha de um nó de muitos é inevitável com qualquer MTBF para um único nó); você pode aproveitar isso para o seu ambiente.

Aqui estão algumas idéias:

  • Certifique-se de que todo o seu hardware seja replicado nvezes (onde né maior que 2 e, de preferência, ímpar) e que cada elemento de hardware possa se comunicar um com o outro. A Ethernet é uma maneira óbvia de fazer isso, mas existem muitas outras rotas muito mais simples que dariam melhor proteção (por exemplo, CAN). Minimize os componentes comuns (até fontes de alimentação). Isso pode significar amostragem de entradas ADC em vários locais, por exemplo.

  • Verifique se o estado do seu aplicativo está em um único local, por exemplo, em uma máquina de estados finitos. Isso pode ser totalmente baseado em RAM, embora não exclua o armazenamento estável. Assim, ele será armazenado em vários locais.

  • Adote um protocolo de quorum para alterações de estado. Veja RAFT, por exemplo. Enquanto você trabalha em C ++, existem bibliotecas conhecidas para isso. Mudanças no FSM somente seriam feitas quando a maioria dos nós concordasse. Use uma biblioteca boa conhecida para a pilha de protocolos e o quorum, em vez de rolar uma, ou todo o seu bom trabalho de redundância será desperdiçado quando o protocolo de quorum for interrompido.

  • Verifique se você soma a verificação (por exemplo, CRC / SHA) ao seu FSM e armazena o CRC / SHA no próprio FSM (além de transmitir na mensagem e somar as próprias mensagens). Faça com que os nós verifiquem seu FSM regularmente com relação a essa soma de verificação, mensagens recebidas da soma de verificação e verifique se a soma de verificação corresponde à soma de verificação do quorum.

  • Crie o maior número possível de verificações internas em seu sistema, fazendo com que os nós que detectam sua própria falha sejam reinicializados (isso é melhor do que continuar trabalhando metade, desde que você tenha nós suficientes). Tente deixá-los remover-se do quorum de maneira limpa durante a reinicialização, caso não voltem a aparecer. Na reinicialização, faça com que eles somam a imagem do software (e qualquer outra coisa que eles carregam) e façam um teste de RAM completo antes de se reintroduzirem no quorum.

  • Use o hardware para apoiá-lo, mas faça-o com cuidado. Você pode obter RAM de ECC, por exemplo, e ler / gravar regularmente nela para corrigir erros de ECC (e entrar em pânico se o erro for incorreto). No entanto (a partir da memória), a RAM estática é muito mais tolerante à radiação ionizante do que a DRAM em primeiro lugar; portanto, pode ser melhor usar a DRAM estática. Veja o primeiro ponto em 'coisas que eu não faria' também.

Digamos que você tenha 1% de chance de falha de qualquer nó em um dia e vamos fingir que você pode tornar as falhas totalmente independentes. Com 5 nós, você precisará de três falhas no prazo de um dia, o que representa uma chance de 0,001%. Com mais, bem, você entendeu a idéia.

Coisas que eu não faria:

  • Subestime o valor de não ter o problema para começar. A menos que o peso seja uma preocupação, um grande bloco de metal em torno do seu dispositivo será uma solução muito mais barata e mais confiável do que uma equipe de programadores pode criar. O acoplamento óptico de entradas de EMI é um problema, etc. Seja como for, tente ao fornecer seus componentes para obter os que são classificados como melhores contra radiação ionizante.

  • Role seus próprios algoritmos . As pessoas já fizeram isso antes. Use o trabalho deles. Tolerância a falhas e algoritmos distribuídos são difíceis. Use o trabalho de outras pessoas sempre que possível.

  • Use configurações complicadas do compilador na esperança ingênua de detectar mais falhas. Se você tiver sorte, poderá detectar mais falhas. O mais provável é que você use um caminho de código no compilador que tenha sido menos testado, principalmente se você o tiver rolado.

  • Use técnicas que não foram testadas em seu ambiente. A maioria das pessoas que cria software de alta disponibilidade precisa simular modos de falha para verificar se o seu HA funciona corretamente e perder muitos modos de falha como resultado. Você está na posição 'feliz' de ter falhas frequentes sob demanda. Portanto, teste cada técnica e garanta que sua aplicação real melhore o MTBF em uma quantidade que exceda a complexidade para introduzi-lo (com a complexidade vem os bugs). Aplique isso especialmente aos meus algoritmos de quorum, etc.


2
Provavelmente, a Ethernet não é uma boa idéia para uso em aplicativos de missão crítica. O I2C também não está fora do PCB. Algo robusto como o CAN seria muito mais adequado.
Lundin

1
@Lundin Fair point, embora qualquer coisa que esteja conectada oticamente (incluindo ethernet) esteja bem.
abligh

1
A mídia física não é tanto a razão pela qual a Ethernet é inadequada, mas a falta de comportamento determinístico em tempo real. Embora eu suponha que hoje em dia também haja maneiras de fornecer Ethernet um tanto confiável, eu apenas a agrupo com a eletrônica comercial / de brinquedo fora do antigo hábito.
Lundin

1
@Lundin esse é um ponto justo, mas como eu estou sugerindo usá-lo para executar o RAFT, haverá um comportamento em tempo real (teoricamente) não determinístico no algoritmo de qualquer maneira (por exemplo, eleições simultâneas para líderes, resultando em uma nova execução semelhante ao CSMA / CD). Se for necessário um comportamento rigoroso em tempo real, é possível que minha resposta tenha mais problemas do que a Ethernet (e observe que, no início da minha resposta, eu disse que 'correto' provavelmente custaria frequentemente 'rápido'). Eu incorporei o seu ponto re PODE embora.
abligh

1
@Lundin: Nenhum sistema que envolva aspectos assíncronos pode ser totalmente não determinístico. Eu acho que o pior comportamento da Ethernet pode ser limitado na ausência de interrupções de hardware se os protocolos de software forem configurados de maneira adequada e os dispositivos tiverem IDs exclusivos e houver um limite conhecido para o número de dispositivos (quanto mais dispositivos, maior o pior número de tentativas).
Supercat

23

Como você solicita especificamente soluções de software e usa C ++, por que não usar a sobrecarga do operador para criar seus próprios tipos de dados seguros? Por exemplo:

Em vez de usar uint32_t(e double, int64_tetc), fazer o seu próprio SAFE_uint32_tque contém um múltiplo (mínimo de 3) de uint32_t. Sobrecarregar todas as operações que você deseja (* + - / << >> = ==! = Etc) para executar e fazer com que as operações sobrecarregadas sejam executadas independentemente em cada valor interno, ou seja, não faça uma vez e copie o resultado. Antes e depois, verifique se todos os valores internos correspondem. Se os valores não corresponderem, você poderá atualizar o incorreto para o valor com o mais comum. Se não houver um valor mais comum, você poderá notificar com segurança que há um erro.

Dessa forma, não importa se a corrupção ocorre na ALU, nos registros, na RAM ou em um barramento, você ainda terá várias tentativas e uma chance muito boa de detectar erros. Observe, no entanto, que isso funciona apenas para as variáveis ​​que você pode substituir - seu ponteiro de pilha, por exemplo, ainda estará suscetível.

Uma história paralela: encontrei um problema semelhante, também em um chip ARM antigo. Acabou sendo uma cadeia de ferramentas que usava uma versão antiga do GCC que, juntamente com o chip específico que usamos, acionava um bug em certos casos extremos que (às vezes) corrompiam valores sendo passados ​​para as funções. Certifique-se de que seu dispositivo não tenha problemas antes de culpá-lo pela atividade de rádio e, sim, às vezes é um bug do compilador =)


1
Algumas dessas sugestões têm algo ao longo de uma 'verificação de sanidade multi-bit' mentalidade semelhante para detectar a corrupção, eu realmente como este com a sugestão de tipos de dados personalizados de segurança crítica a mais embora
WearyWanderer

2
Existem sistemas no mundo em que cada nó redundante foi projetado e desenvolvido por equipes diferentes, com um árbitro para garantir que eles não se instalem acidentalmente nas mesmas soluções. Dessa forma, você não terá todos eles desativados pelo mesmo bug e transientes semelhantes não manifestam modos de falha semelhantes.
jwdonahue

16

Isenção de responsabilidade: não sou profissional de radioatividade nem trabalhei para esse tipo de aplicação. Mas trabalhei com erros leves e redundância no arquivamento de longo prazo de dados críticos, que estão um pouco vinculados (mesmo problema, objetivos diferentes).

O principal problema com a radioatividade, na minha opinião, é que a radioatividade pode alternar bits, portanto a radioatividade pode / irá alterar qualquer memória digital . Esses erros são geralmente chamados de erros leves , podridão de bits etc.

A questão é: como calcular de forma confiável quando sua memória não é confiável?

Para reduzir significativamente a taxa de erros de software (às custas da sobrecarga computacional, pois serão principalmente soluções baseadas em software), você pode:

  • confiar no bom e velho esquema de redundância e, mais especificamente, nos códigos de correção de erros mais eficientes (mesma finalidade, mas algoritmos mais inteligentes, para que você possa recuperar mais bits com menos redundância). Isso às vezes (incorretamente) também é chamado de soma de verificação. Com esse tipo de solução, você terá que armazenar o estado completo do seu programa a qualquer momento em uma variável / classe mestre (ou uma estrutura?), Calcular um ECC e verificar se o ECC está correto antes de fazer qualquer coisa, e se não, repare os campos. Essa solução, no entanto, não garante que seu software possa funcionar (simplesmente funcionará corretamente quando puder, ou parará de funcionar, se não, porque o ECC pode informar se algo está errado) e, nesse caso, você pode parar o software para que você não obtenha resultados falsos).

  • ou você pode usar estruturas de dados algorítmicas resilientes, que garante, até certo ponto, que seu programa ainda fornecerá resultados corretos, mesmo na presença de erros de software. Esses algoritmos podem ser vistos como uma mistura de estruturas algorítmicas comuns com esquemas de ECC nativamente misturados, mas isso é muito mais resistente do que isso, porque o esquema de resiliência está fortemente ligado à estrutura, para que você não precise codificar procedimentos adicionais para verificar o ECC, e geralmente eles são muito mais rápidos. Essas estruturas fornecem uma maneira de garantir que seu programa funcione sob qualquer condição, até o limite teórico de erros leves. Você também pode misturar essas estruturas resilientes com o esquema de redundância / ECC para obter segurança adicional (ou codificar suas estruturas de dados mais importantes como resilientes e o restante, os dados dispensáveis ​​que você pode recompilar das principais estruturas de dados,

Se você estiver interessado em estruturas de dados resilientes (que é um campo novo, mas interessante, em engenharia de algoritmos e redundância), recomendamos que você leia os seguintes documentos:

  • Introdução de estruturas de dados de algoritmos resilientes por Giuseppe F.Italiano, Universidade de Roma "Tor Vergata"

  • Christiano, P., Demaine, ED; Kishore, S. (2011). Estruturas de dados tolerantes a falhas e sem perdas com sobrecarga aditiva. Em Algoritmos e Estruturas de Dados (pp. 243-254). Springer Berlin Heidelberg.

  • Ferraro-Petrillo, U., Grandoni, F., & Italiano, GF (2013). Estruturas de dados resilientes a falhas de memória: um estudo experimental de dicionários. Journal of Experimental Algorithmics (JEA), 18, 1-6.

  • Italiano, GF (2010). Algoritmos e estruturas de dados resilientes. Em Algoritmos e Complexidade (pp. 13-24). Springer Berlin Heidelberg.

Se você estiver interessado em saber mais sobre o campo de estruturas de dados resilientes, pode fazer o checkout dos trabalhos de Giuseppe F. Italiano (e trabalhar o seu caminho através das referências) e o modelo de falha de RAM (introduzido em Finocchi et al. 2005; Finocchi e Italiano 2008).

/ EDIT: Ilustrei a prevenção / recuperação de erros de software principalmente para memória RAM e armazenamento de dados, mas não falei sobre erros de computação (CPU) . Outras respostas já apontaram para o uso de transações atômicas, como nos bancos de dados, por isso proponho outro esquema mais simples: redundância e voto majoritário .

A idéia é que você simplesmente faça x vezes o mesmo cálculo para cada cálculo que precisa e armazene o resultado em x variáveis ​​diferentes (com x> = 3). Você pode comparar suas variáveis ​​x :

  • se todos concordarem, não haverá nenhum erro de computação.
  • se eles discordarem, você poderá usar uma votação majoritária para obter o valor correto e, como isso significa que o cálculo foi parcialmente corrompido, você também poderá acionar uma verificação de estado do sistema / programa para verificar se o restante está correto.
  • se a votação majoritária não puder determinar um vencedor (todos os valores de x são diferentes), é um sinal perfeito para você ativar o procedimento à prova de falhas (reinicializar, emitir um alerta ao usuário etc.).

Esse esquema de redundância é muito rápido comparado ao ECC (praticamente O (1)) e fornece um sinal claro quando você precisa de segurança . O voto da maioria também é (quase) garantido para nunca produzir resultados corrompidos e também para recuperar-se de pequenos erros de computação , porque a probabilidade de que x computações a mesma saída é infinitesimal (porque há uma quantidade enorme de saídas possíveis, é quase impossível obter aleatoriamente 3 vezes o mesmo, ainda menos chances se x> 3).

Portanto, com o voto da maioria, você está protegido contra saída corrompida e com redundância x == 3, você pode recuperar 1 erro (com x == 4, serão 2 erros recuperáveis ​​etc.) - a equação exata é nb_error_recoverable == (x-2)onde x é o número de repetições de cálculo porque você precisa de pelo menos 2 cálculos concordantes para se recuperar usando o voto da maioria).

A desvantagem é que você precisa calcular x vezes em vez de uma vez, para ter um custo de computação adicional, mas a complexidade linear é tão assintoticamente que você não perde muito pelos benefícios que obtém. Uma maneira rápida de votar por maioria é calcular o modo em uma matriz, mas você também pode usar um filtro mediano.

Além disso, se você deseja ter certeza de que os cálculos são conduzidos corretamente, se você pode criar seu próprio hardware, pode construir seu dispositivo com x CPUs e conectar o sistema para que os cálculos sejam duplicados automaticamente nas x CPUs com uma votação majoritária. mecanicamente no final (usando portas AND / OR, por exemplo). Isso geralmente é implementado em aviões e dispositivos de missão crítica (consulte redundância modular tripla ). Dessa forma, você não teria nenhuma sobrecarga computacional (já que os cálculos adicionais serão feitos em paralelo) e você terá outra camada de proteção contra erros de software (uma vez que a duplicação do cálculo e a votação majoritária serão gerenciados diretamente pelo hardware e não pelo software - que pode ser corrompido mais facilmente, pois um programa é simplesmente bits armazenados na memória ...).


9

Um ponto que ninguém parece ter mencionado. Você diz que está desenvolvendo no GCC e fazendo compilação cruzada no ARM. Como você sabe que não possui um código que faça suposições sobre RAM livre, tamanho inteiro, tamanho do ponteiro, quanto tempo leva para realizar uma determinada operação, quanto tempo o sistema funcionará continuamente ou várias coisas assim? Este é um problema muito comum.

A resposta é geralmente teste de unidade automatizado. Escreva chicotes de teste que exercitam o código no sistema de desenvolvimento e execute os mesmos chicotes de teste no sistema de destino. Procure diferenças!

Verifique também se há erratas no seu dispositivo incorporado. Você pode achar que há algo sobre "não faça isso porque ele falhará; portanto, habilite essa opção do compilador e o compilador funcionará com isso".

Em resumo, sua fonte mais provável de falha são os erros no seu código. Até você ter certeza de que esse não é o caso, não se preocupe (ainda) com modos de falha mais esotéricos.


1
De fato, em nenhum lugar do teste da pergunta o autor menciona que o aplicativo foi executado muito bem fora do ambiente radioativo.
Marc.2377

9

Você deseja mais de 3 máquinas escravas com um mestre fora do ambiente de radiação. Todas as E / S passam pelo mestre que contém um mecanismo de votação e / ou nova tentativa. Os escravos devem ter um watchdog de hardware cada um e a chamada para batê-los deve ser cercada por CRCs ou similares para reduzir a probabilidade de choques involuntários. O bumping deve ser controlado pelo mestre, para que a conexão perdida com o mestre seja igual à reinicialização em alguns segundos.

Uma vantagem dessa solução é que você pode usar a mesma API para o mestre e para os escravos, para que a redundância se torne um recurso transparente.

Editar: A partir dos comentários, sinto a necessidade de esclarecer a "ideia da CRC". A possibilidade do escravo esbarrar em seu próprio cão de guarda é quase zero se você cercar o solavanco com CRC ou digerir verificações em dados aleatórios do mestre. Esses dados aleatórios são enviados apenas pelo mestre quando o escravo sob escrutínio está alinhado com os outros. Os dados aleatórios e CRC / resumo são limpos imediatamente após cada aumento. A frequência de resposta do mestre-escravo deve ser mais que o dobro do tempo limite do watchdog. Os dados enviados pelo mestre são gerados exclusivamente todas as vezes.


7
Estou tentando entender um cenário em que você pode ter um mestre fora do ambiente de radiação, capaz de se comunicar de maneira confiável com os escravos dentro do ambiente de radiação, onde você não pode simplesmente colocar os escravos fora do ambiente de radiação.
Fostandy

1
@ fostandy: Os escravos estão medindo ou controlando usando equipamentos que precisam de um controlador. Diga um contador geiger. O mestre não precisa de comunicação confiável devido à redundância do escravo.
Jonas Byström

4
A introdução de um mestre não significa automaticamente maior segurança. Se o escravo x enlouqueceu devido à corrupção da memória, de modo que se diz repetidamente "mestre está aqui, mestre está feliz", então nenhuma quantidade de CRCs ou ordens latidas do mestre o salvará. Você teria que dar ao mestre a possibilidade de cortar o poder daquele escravo. E se você tiver um erro de causa comum, adicionar mais escravos não aumentará a segurança. Lembre-se também de que a quantidade de bugs de software e a quantidade de coisas que podem ser quebradas aumentam com a complexidade.
Lundin

5
Dito isto, é claro que seria bom "terceirizar" o máximo do programa para um lugar menos exposto, mantendo os eletrônicos no ambiente radioativo o mais simples possível, se você tiver essa opção.
Lundin

7

Que tal executar muitas instâncias do seu aplicativo. Se as falhas ocorrerem devido a alterações aleatórias nos bits de memória, é provável que algumas das instâncias de seu aplicativo passem e produzam resultados precisos. Provavelmente é bastante fácil (para alguém com experiência estatística) calcular quantas instâncias você precisa, dada a probabilidade de um flop de bits, para obter o menor erro geral que desejar.


2
Certamente, um sistema embarcado prefere capturas críticas de segurança em uma instância de um aplicativo robusto do que apenas disparar várias instâncias, aumentando os requisitos de hardware e, de certa forma, esperando com pouca sorte que pelo menos uma instância seja aprovada? Tenho a ideia e é válida, mas se inclinar mais para as sugestões que não dependem de força bruta
WearyWanderer

7

O que você pergunta é um tópico bastante complexo - não é fácil de responder. Outras respostas estão ok, mas cobriram apenas uma pequena parte de todas as coisas que você precisa fazer.

Como visto nos comentários , não é possível corrigir 100% dos problemas de hardware; no entanto, é possível com alta probabilidade reduzi-los ou capturá-los usando várias técnicas.

Se eu fosse você, criaria o software do mais alto nível de integridade de segurança (SIL-4). Obtenha o documento IEC 61513 (para a indústria nuclear) e siga-o.


11
Ou melhor, leia os requisitos técnicos e implemente os que fazem sentido. Uma grande parte dos padrões SIL não faz sentido, se você os seguir dogmaticamente, acabará com produtos inseguros e perigosos. Hoje, a certificação SIL é principalmente produzir uma tonelada de documentação e depois subornar uma casa de teste. O nível SIL não diz nada sobre a segurança real do sistema. Em vez disso, você deve se concentrar nas medidas técnicas de segurança reais. Existem alguns muito bons nos documentos SIL e outros completos e sem sentido.
Lundin

7

Alguém mencionou o uso de chips mais lentos para impedir que os íons lançassem bits tão facilmente. De maneira semelhante, talvez use um processador / ram especializado que realmente use vários bits para armazenar um único bit. Assim, fornecendo uma tolerância a falhas de hardware, porque seria muito improvável que todos os bits fossem invertidos. Então 1 = 1111, mas precisaria ser atingido 4 vezes para realmente virar. (4 pode ser um número ruim, pois se 2 bits forem invertidos, isso já é ambíguo). Portanto, se você optar por 8, obtém 8 vezes menos memória RAM e um pouco mais de tempo de acesso, mas uma representação de dados muito mais confiável. Você provavelmente poderia fazer isso tanto no nível do software com um compilador especializado (alocar x quanto mais espaço para tudo) ou na implementação da linguagem (escrever wrappers para estruturas de dados que alocam as coisas dessa maneira).


7

Talvez ajude a saber se isso significa que o hardware seja "projetado para este ambiente". Como isso corrige e / ou indica a presença de erros do SEU?

Em um projeto relacionado à exploração espacial, tínhamos um MCU personalizado, que geraria uma exceção / interrupção nos erros do SEU, mas com algum atraso, ou seja, alguns ciclos podem passar / as instruções serão executadas após o insn que causou a exceção do SEU.

Particularmente vulnerável era o cache de dados; portanto, um manipulador invalidaria a linha de cache incorreta e reiniciaria o programa. Somente que, devido à natureza imprecisa da exceção, a sequência de insns encabeçada pela exceção que gera insn pode não ser reinicializável.

Identificamos as sequências perigosas (não reinicializáveis) (como lw $3, 0x0($2), seguidas por um insn, que modifica $2e não depende de dados $3), e eu fiz modificações no GCC, para que essas sequências não ocorram (por exemplo, como último recurso, separando o dois insns por a nop).

Apenas algo a considerar ...


7

Se o seu hardware falhar, você poderá usar o armazenamento mecânico para recuperá-lo. Se sua base de código for pequena e tiver algum espaço físico, você poderá usar um armazenamento de dados mecânico.

Digite a descrição da imagem aqui

Haverá uma superfície de material que não será afetada pela radiação. Várias engrenagens estarão lá. Um leitor mecânico funcionará em todas as marchas e será flexível para subir e descer. Para baixo significa que é 0 e para cima significa que é 1. De 0 e 1, você pode gerar sua base de código.


2
Talvez um meio óptico como um CD-ROM atenda a essa definição. Teria o bônus adicional de uma grande capacidade.
Wossname 28/04

2
Sim, será semelhante, mas o CD-ROM usará menos, mas este será um sistema totalmente mecânico.
Hitul 29/04

7
Gostaria de saber se existe uma razão para que eles não usem leitores de cartões perfurados no espaço.
Soren

3
@Soren Velocidade e espaço físico podem ser uma razão.
Hitul 04/04

5

Use um planejador cíclico . Isso permite adicionar tempos de manutenção regulares para verificar a correção dos dados críticos. O problema mais frequentemente encontrado é a corrupção da pilha. Se o seu software for cíclico, você poderá reinicializar a pilha entre os ciclos. Não reutilize as pilhas para chamadas de interrupção, configure uma pilha separada de cada chamada de interrupção importante.

Semelhante ao conceito Watchdog são os cronômetros de prazo. Inicie um temporizador de hardware antes de chamar uma função. Se a função não retornar antes que o cronômetro do prazo seja interrompido, recarregue a pilha e tente novamente. Se ainda assim falhar após 3/5 tentativas, você precisará recarregar a partir da ROM.

Divida seu software em partes e isole essas partes para usar áreas de memória e tempos de execução separados (especialmente em um ambiente de controle). Exemplo: aquisição de sinal, dados de posse, algoritmo principal e implementação / transmissão de resultados. Isso significa que uma falha em uma parte não causará falhas no restante do programa. Portanto, enquanto reparamos a aquisição do sinal, o restante das tarefas continua com dados obsoletos.

Tudo precisa de CRCs. Se você executar fora da RAM, mesmo o seu .text precisará de um CRC. Verifique os CRCs regularmente se estiver usando um planejador cíclico. Alguns compiladores (não o GCC) podem gerar CRCs para cada seção e alguns processadores têm hardware dedicado para fazer cálculos de CRC, mas acho que isso ficaria fora do escopo da sua pergunta. A verificação de CRCs também solicita que o controlador ECC na memória repare erros de bit único antes que se torne um problema.


4

Em primeiro lugar, projete seu aplicativo para evitar falhas . Certifique-se de que, como parte da operação de fluxo normal, ele espere redefinir (dependendo da sua aplicação e do tipo de falha, suave ou dura). É difícil ficar perfeito: operações críticas que exigem algum grau de transacionalidade podem precisar ser verificadas e ajustadas em um nível de montagem, para que uma interrupção em um ponto-chave não possa resultar em comandos externos inconsistentes. Falha rapidamente assim que qualquer corrupção irrecuperável da memória ou desvio de fluxo de controle é detectado. Falhas de log, se possível.

Em segundo lugar, sempre que possível, corrija a corrupção e continue . Isso significa soma de verificação e correção de tabelas constantes (e código de programa, se você puder) com frequência; talvez antes de cada operação principal ou em uma interrupção cronometrada e armazenando variáveis ​​em estruturas que se autocorrigam (novamente antes de cada operação principal ou em uma interrupção cronometrada, faça uma votação majoritária de 3 e corrija se houver um único desvio). Correções de log, se possível.

Terceiro, falha no teste . Configure um ambiente de teste repetitivo que inverta os bits na memória aleatoriamente. Isso permitirá que você replique situações de corrupção e ajude a projetar seu aplicativo em torno delas.


3

Dados os comentários de supercat, as tendências dos compiladores modernos e outras coisas, eu ficaria tentado a voltar aos dias antigos e escrever todo o código em alocações de montagem e memória estática em todos os lugares. Para esse tipo de confiabilidade total, acho que a montagem não incorre mais em uma grande diferença percentual do custo.


Sou um grande fã da linguagem assembly (como você pode ver nas minhas respostas a outras perguntas), mas não acho que seja uma boa resposta. É bem possível saber o que esperar do compilador para a maioria dos códigos C (em termos de valores que vivem em registradores versus memória) e você sempre pode verificar se é o que esperava. Escrever manualmente um grande projeto no asm é apenas uma tonelada de trabalho extra, mesmo se você tiver desenvolvedores muito confortáveis ​​em escrever o ARM asm. Talvez se você quiser fazer coisas como calcular o mesmo resultado três vezes, escrever algumas funções no asm faz sentido. (compiladores CSE-lo afastado)
Peter Cordes

Caso contrário, o risco mais alto que precisa ser compensado é a atualização do compilador, deixando-o com alterações inesperadas.
Joshua

1

Aqui estão muitas respostas, mas tentarei resumir minhas idéias sobre isso.

Algo trava ou não funciona corretamente pode ser resultado de seus próprios erros - então deve ser facilmente corrigido quando você localizar o problema. Mas há também a possibilidade de falhas de hardware - e isso é difícil, se não impossível, de corrigir em geral.

Eu recomendaria primeiro tentar capturar a situação problemática registrando (pilha, registradores, chamadas de função) - registrando-as em algum lugar no arquivo ou transmitindo-as de alguma maneira diretamente ("oh não - estou travando").

A recuperação dessa situação de erro é reinicializada (se o software ainda estiver ativo e funcionando) ou redefinida por hardware (por exemplo, hw watchdogs). Mais fácil de começar do primeiro.

Se o problema estiver relacionado ao hardware - o registro deve ajudá-lo a identificar em qual problema de chamada de função ocorre e que pode fornecer informações internas sobre o que não está funcionando e onde.

Além disso, se o código for relativamente complexo - faz sentido "dividi-lo e conquistá-lo" - o que significa que você remove / desativa algumas chamadas de função nas quais suspeita de problemas - normalmente desabilitando metade do código e habilitando outra metade - você pode obter "funciona" / tipo de decisão "não funciona", após o qual você pode se concentrar em outra metade do código. (Onde está o problema)

Se o problema ocorrer após algum tempo - pode haver suspeita de estouro de pilha - é melhor monitorar os registros de ponto de pilha - se eles crescerem constantemente.

E se você conseguir minimizar completamente o seu código até o tipo de aplicativo "olá mundo" - e ainda falhar aleatoriamente - são esperados problemas de hardware - e é preciso haver "atualização de hardware" - o que significa inventar tais cpu / ram / ... - combinação de hardware que toleraria melhor a radiação.

O mais importante é provavelmente como você recupera seus logs se a máquina totalmente parada / zerada / não funcionar - provavelmente a primeira coisa que o boottap deve fazer - é voltar para casa se houver uma situação problemática.

Se for possível no seu ambiente também transmitir um sinal e receber resposta - você pode tentar criar algum tipo de ambiente de depuração remota on-line, mas deve ter pelo menos a mídia de comunicação funcionando e algum processador / alguma memória RAM no estado de funcionamento. E por depuração remota, quero dizer o tipo de abordagem GDB / gdb stub ou a sua própria implementação do que você precisa recuperar do seu aplicativo (por exemplo, baixar arquivos de log, baixar pilha de chamadas, baixar ram, reiniciar)


Desculpe, mas a pergunta é sobre o ambiente radioativo onde ocorrerão falhas de hardware. Sua resposta é sobre otimização geral de software e como encontrar bugs. Mas nesta situação, as falhas não são produzidos por insetos
jeb

Sim, você também pode culpar a gravidade da terra, otimizações do compilador, biblioteca de terceiros, ambiente radioativo e assim por diante. Mas você tem certeza de que não são seus próprios erros? :-) A menos que comprovado - eu não acredito. Eu executei uma vez uma situação de atualização de firmware e teste de desligamento automático - meu software sobreviveu a todas as situações de desligamento automático somente depois de corrigir todos os meus próprios erros. (Mais de 4000 desligamentos durante o período noturno) Mas é difícil acreditar que houve bug em alguns casos. Especialmente quando estamos falando sobre corrupção de memória.
TarmoPikaro

0

Eu realmente li muitas ótimas respostas!

Aqui estão meus 2 centavos: construa um modelo estatístico da anormalidade da memória / registro, escrevendo um software para verificar a memória ou realizar comparações frequentes de registros. Além disso, crie um emulador no estilo de uma máquina virtual onde você pode experimentar o problema. Eu acho que se você variar o tamanho da junção, a frequência do relógio, o fornecedor, a caixa, etc., observará um comportamento diferente.

Até a memória do nosso PC de mesa apresenta uma certa taxa de falhas, o que, no entanto, não prejudica o dia a dia do trabalho.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.