Estoy aprendiendo métodos formales. Escuché que se usan métodos formales (y generalmente solo se usan) para crear software de misión crítica (como el controlador del reactor nuclear, el controlador de vuelo de la aeronave, el controlador de la sonda espacial). Por eso me interesa aprenderlo: p
Sin embargo, después de aprender métodos formales (especialmente LTL, CTL y sus hermanos), creo que solo pueden usarse para verificar la exactitud de la especificación (seguridad, vida, equidad, etc.).
Pero, ¿cómo verificar que el software (no solo la especificación) es correcto?
Descargo de responsabilidad: soy un 90% idiota cuando se trata de informática teórica. Así que por favor sé misericordioso mientras respondes.
Respuestas:
La pregunta es bastante amplia. Para responderlo en un espacio razonable haré muchas simplificaciones excesivas.
Acordemos la terminología. Un programa es correcto cuando implica su especificación. Esta declaración vaga se hace precisa de muchas maneras, al precisar qué es exactamente un programa y qué es exactamente una especificación. Por ejemplo, en la verificación de modelos, el programa es una estructura de Kripke y la especificación es a menudo una fórmula LTL . O bien, el programa podría ser una lista de instrucciones de PowerPC y la especificación podría ser un conjunto de afirmaciones de Hoare-Floyd escritas, digamos, en lógica de primer orden. Hay muchas variaciones posibles. Es tentador concluir que en un caso (estructura de Kripke) no verificamos un programa real, mientras que en el segundo caso (lista de instrucciones de PowerPC) lo hacemos. Sin embargo, es importante darse cuenta de que realmente estamos mirando modelos matemáticos en ambos casos, y esto está perfectamente bien. (La situación es bastante similar a la física donde, por ejemplo, la mecánica clásica es un modelo matemático de la realidad).
La mayoría de las formalizaciones distinguen entre la sintaxis y la semántica de un programa; es decir, cómo se representa y qué significa. La semántica de un programa es lo que cuenta desde el punto de vista de la verificación del programa. Pero, por supuesto, es importante tener una forma clara de asignar significados a (representaciones sintácticas de) programas. Dos formas populares son las siguientes:
(Hay otros. Me siento particularmente mal por omitir la semántica denotativa, pero esta respuesta ya es larga.) El código de máquina más la semántica operativa está bastante cerca de lo que la mayoría de la gente llamaría un "programa real". Aquí hay un documento seminal, que utiliza la semántica operativa para un subconjunto del código de máquina DEC Alpha:
¿Por qué usarías alguna semántica de nivel superior, como las axiomáticas? Cuando no desea que su prueba de corrección dependa del hardware en el que se ejecuta. El enfoque es demostrar la corrección de un algoritmo con respecto a alguna semántica conveniente de alto nivel, y luego probar que la semántica suena con respecto a la semántica de nivel inferior que está más cerca de las máquinas reales.
En resumen, podría pensar en tres razones que llevaron a su pregunta:
Esta respuesta simplemente trata de identificar tres formas diferentes en que entendí la pregunta. Profundizar en cualquiera de estos puntos requeriría mucho espacio.
fuente
Un enfoque para reducir la brecha entre un programa y su especificación es utilizar un lenguaje con una semántica formal. Un ejemplo interesante aquí sería Esterel . Eche un vistazo a la página web de Gérard Berry para ver algunas charlas interesantes sobre su trabajo de llevar métodos formales al mundo real. http://www-sop.inria.fr/members/Gerard.Berry/
ps ¿Has estado en un Airbus? ¡Has volado con métodos formales!
fuente
La ciencia de la construcción de software confiable en el "mundo real" todavía se está desarrollando y está en cierta medida al borde de un estudio inherentemente cultural o antropológico, porque las computadoras y el software no "causan" errores, ¡los humanos sí! Esta respuesta se centrará en los enfoques generales de preguntas y respuestas, de los cuales la verificación formal del software puede verse como un elemento.
Una observación notable es que a menudo el software que es "suficientemente bueno" pero "defectuoso" con frecuencia puede vender más que el software mejor probado pero de menor funcionalidad en el mercado. en otras palabras, el mercado no siempre otorga una importancia superior a la calidad del software y las técnicas modernas de ingeniería de software, que no siempre enfatizan la calidad, sino que reflejan eso. Además, la calidad a menudo puede agregar un gasto significativo al producto final. con esas advertencias, estos son algunos de los conceptos básicos:
Sistemas redundantes / tolerantes a fallas. Esta es un área amplia de estudio. La tolerancia a fallos y la redundancia se pueden diseñar en las muchas capas de un sistema. por ejemplo, un enrutador, un servidor, una unidad de disco, etc.
las pruebas . todos los tipos: pruebas unitarias, pruebas de integración, pruebas de aceptación del usuario, pruebas de regresión, etc.
Hoy en día, las pruebas automatizadas a través de conjuntos de pruebas que se pueden ejecutar sin supervisión son más desarrolladas / importantes. Las suites de prueba en ejecución a menudo se combinan con la herramienta de compilación.
Un concepto importante en las pruebas es la cobertura del código . es decir, qué código ejerce la prueba. una prueba no puede encontrar un error en el código que no haya sido "tocado" por la prueba.
Otro concepto clave en las pruebas es probar el arnés que ejercita el código al que no se puede acceder fácilmente directamente.
Las pruebas deben ejercitar todos los niveles del software. Si el software está bien modularizado, esto no es difícil. Las pruebas de nivel superior deben penetrar profundamente en el código. Las pruebas que ejercen grandes cantidades de código con una configuración de prueba pequeña aumentan el "apalancamiento de prueba" .
hacer que el código sea lo menos complicado posible es importante para las pruebas. Las pruebas deben ser una consideración en el diseño de la arquitectura. a menudo hay varias formas de implementar la misma función, pero algunas tienen implicaciones muy diferentes para la cobertura / apalancamiento de la prueba. para cada rama en el código, a menudo es otro caso de prueba. las ramas dentro de las ramas aumentan exponencialmente en las rutas de código. por lo tanto, evitar la lógica altamente anidada / condicional mejora la capacidad de prueba.
estudiar fallas de software famosas (masivas) de las cuales hay muchos ejemplos y estudios de casos es útil para comprender la historia y desarrollar una mentalidad orientada hacia consideraciones de calidad.
¡uno puede dejarse llevar por las pruebas! Existe un problema con muy poca o demasiada prueba. hay un "punto dulce". el software no se puede construir con éxito en ninguno de los extremos.
use todas las herramientas básicas de la manera más efectiva. depuradores, perfiladores de código, herramientas de cobertura de código de prueba, sistema de seguimiento de defectos, etc. no necesariamente se compromete a arreglar, pero rastrea incluso los defectos más pequeños en el software de seguimiento.
El uso cuidadoso de SCM, la gestión del código fuente y las técnicas de ramificación es importante para evitar regresiones, aislar y progresar soluciones, etc.
Programación en versión N : una práctica utilizada a menudo para desarrollar software de misión crítica. La premisa de esta práctica es que es poco probable que N programas desarrollados independientemente tengan el mismo error / falla común. Esto ha sido criticado en algunos artículos . NVP es, sin embargo, una práctica, no un concepto teórico.
Creo que el físico Feynman tiene una explicación del método que utilizó la NASA para garantizar la confiabilidad de los sistemas de transbordadores espaciales en su libro "¿Qué le importa lo que piensen los demás?" - Dijo que tenían dos equipos, digamos el Equipo A y el Equipo B. El equipo A construyó el software. El equipo B adoptó un enfoque de confrontación con el equipo A e intentó romper el software.
ayuda si el Equipo B tiene buenos antecedentes en ingeniería de software, es decir, ellos mismos pueden escribir arneses de código / pruebas programáticas, etc. en ese caso, el Equipo B tenía casi el mismo nivel de recursos que el Equipo A. Por otro lado, este enfoque es costoso porque casi puede duplicar el costo de construir el software. más típicamente, hay un equipo de control de calidad más pequeño en comparación con el equipo de desarrollo.
fuente
Un enfoque antiguo (pero todavía se usa en algunas aplicaciones) es la programación de la versión N
De Wikipedia:
La programación de la versión N ( NVP ), también conocida como programación multiversion , es un método o proceso en la ingeniería de software donde se generan múltiples programas funcionalmente equivalentes independientemente de las mismas especificaciones iniciales. El concepto de programación de la versión N fue introducido en 1977 por Liming Chen y Algirdas Avizienis con la conjetura central de que "la independencia de los esfuerzos de programación reducirá en gran medida la probabilidad de que ocurran fallas de software idénticas en dos o más versiones del programa". de NVP es mejorar la confiabilidad de la operación del software incorporando tolerancia a fallas o redundancia.
....
Ver por ejemplo: " Desafíos en la construcción de fallas - Sistema de control de vuelo tolerante para una aeronave civil "
fuente
Fajrian, esta pregunta que ha hecho cubre dos de los mayores problemas en la investigación del ingeniero de software: la conformidad entre la especificación y el modelo y entre el modelo y el código. Modele aquí una representación de lo que hará el sistema, o cómo se hará, hay muchos niveles para modelar un sistema.
Entonces, hay algunas personas que intentan encontrar la mejor respuesta a su pregunta. Debido a que es muy difícil verificar la corrección de un software basado en un modelo, por ejemplo, utilizando métodos formales. Sé que JML es una forma de hacerlo, pero no conozco los límites de su uso.
Para concluir, cómo es difícil hacer la verificación de la corrección del código, las personas intentan mezclar métodos formales y pruebas, creando pruebas automáticamente a partir de especificaciones, por ejemplo. Un ejemplo para los sistemas en tiempo real son los TIOSTS que se basan en eventos temporizados de entrada / salida.
Probar solo no es un enfoque de método formal, hacerlo mejora la confiabilidad pero no verifica la corrección.
fuente
Hace dos o tres años comencé a analizar los métodos formales aplicados al software. Esta fue una búsqueda impulsada por la curiosidad y por la sensación de que tenía que aprender herramientas y métodos de programación con períodos de tiempo más largos. Aunque soñé con desear un Silver Bullet , realmente pensé que no había una respuesta a la pregunta: "¿Cómo puedo escribir un programa correcto?".
En este punto de la búsqueda después de probar algunas herramientas (Z, B, VHDL y Estelle), estoy usando TLA + . Esta es una variante de la lógica temporal con herramientas de software para la verificación de modelos y pruebas mecánicas. Creo que elegí este enfoque porque L. Lamport lo respaldaba, la sintaxis era simple, había muchos ejemplos, había una comunidad que se encargaba de ello y el lenguaje y las herramientas estaban bastante bien documentados.
Con respecto a mi pregunta inicial, creo que no hay una respuesta completa. Sin embargo, vale la pena aprender que vale la pena especificar formalmente algunas partes de un sistema. También es bastante útil realizar ingeniería inversa en algunos complejos. Es decir, es efectivo crear un plan para las partes difíciles y críticas. Sin embargo, no creo que haya un método efectivo para traducir automáticamente la especificación a un lenguaje o marco de programación (a menos que restrinja el proyecto a un entorno muy específico). Tampoco creo que tener una especificación formal deba impedir que pruebe el software.
En pocas palabras, creo que la siguiente metáfora (de Lamport) es realmente poderosa: "¿Esperas que una casa se construya automáticamente a partir de un plano? ¿Comprarás una casa que aún no está construida y no tiene un plano?" .
Durante esta búsqueda, he encontrado útiles los siguientes recursos:
¡Buena suerte!
fuente
Las respuestas hasta ahora cubrieron la mayor parte de lo que debería decirse sobre los fundamentos de cómo se relacionan la especificación y el código entre sí. Solo quiero agregar un punto más práctico que aborde la pregunta en el encabezado de este hilo:
¿Cómo crear software de misión crítica?
Existen herramientas que analizan automáticamente su código en busca de errores (violaciones de la especificación o "errores típicos"). Que yo sepa, estos métodos se basan principalmente en el análisis estático y no están inmediatamente relacionados con las teorías que mencionó (LTL / CTL / ...), pero sí encuentran errores en el código real y ya es factible, desde un punto de vista práctico. vista, para usar tales herramientas en proyectos industriales. Personalmente no he usado muchos de ellos, pero parece que estas herramientas comienzan a ser aceptadas por los profesionales. Para leer más, puedo recomendar el siguiente artículo de blog:
http://www.altdevblogaday.com/2011/12/24/static-code-analysis/
fuente
Los algoritmos de certificación pueden ser útiles al construir software de misión crítica.
Lea más en este documento de encuesta Algoritmos de certificación de McConnell, RM y Mehlhorn, K. y Naher, S. y Schweitzer, P.
fuente
¿Examen de la unidad? Escriba una prueba para cada requisito en la especificación y luego pruebe cada método en su implementación para ver que su salida / entrada se ajusta a dicha especificación. Esto se puede automatizar para que estas pruebas se ejecuten continuamente para garantizar que ningún cambio rompa nunca las funciones que funcionaban anteriormente.
Teóricamente hablando, si las pruebas de su unidad tienen una cobertura del código del 100% (es decir, se prueba cada método en su código) su software debe ser correcto, siempre que las pruebas en sí sean precisas y realistas.
fuente