image
Маргарет Гамильтон стоит рядом с написанным ей исходным кодом бортового компьютера «Аполлона»


Лаборатория реактивного движения (Jet Propulsion Laboratory) — научно-исследовательский центр НАСА, ответственный за большинство беспилотных космических кораблей США. Там пишут много кода, и права на ошибку у них намного меньше, чем у обычных программистов.


В JPL пишут на Си, и на их сайте есть документ "JPL Institutional Coding Standard", описывающий жесткие стандарты кодирования внутри организации. Они напоминают правила программирования для встроенных (embedded) систем и систем реального времени, с ограниченными ресурсами. Но многие из правил эти просто принципы хорошего программирования. Ограничение сложности, максимальное упрощение для последующего чтения кода и отладки, отсутствие побочных эффектов. Мы в Хекслете постоянно говорим об этом в вебинарах и, конечно, в самих курсах. Мы считаем очень важным как можно раньше поднимать эти темы, поэтому про функции и побочные эффекты начинаем говорить в самом первом курсе «Основы программирования», который рассчитан на новичков. Это бесплатный курс, кстати, и в нем есть практика на языке JavaScript.


Спасибо хабраюзеру Boletus за важную поправку и дополнение:
В 2006 году Gerard Holzmann с коллективом сформулировал 10 основных правил для JPL в документе «The Power of 10: Rules for Developing Safety-Critical Code». Они вошли в основу нынешнего стандарта, наряду с MISRA C и другими дополнениями. Статья в Википедии.


Вот перевод этого списка.


  1. Нужно сильно ограничивать ветвления и условия. Не использовать goto, setjmp или longjmp, не использовать прямую или косвенную рекурсию.


  2. У всех циклов должен быть предел. Проверяющая программа должна иметь возможность легко доказать, что определенное количество итераций не может быть превышено. Если предел невозможно доказать статически, то правило считается нарушенным.


  3. Не использовать динамическое распределение памяти после инициализации.


  4. Любая функция должна уместиться на одном стандартном листе бумаги, одно выражение на строку и одна строка на определение. Обычно это означает, что функция не должна быть длиннее 60 строк.


  5. Должно быть не более двух ассертов на функцию. Ассерты используются для проверки аномальных условий, которые не могут произойти при реальном запуске. Ассерты не должны содержать сайд-эффектов, и по формату должны быть Boolean-тестами. Когда ассерт падает, должно запуститься специальное действие по восстановлению, например, возврат условия падения обратно в вызывающую функцию. Если проверяющая программа доказывает, что ассерт никогда не фейлится или никогда не удовлетворяется, то правило считается нарушенным. (Нельзя обойти это правило с помощью бессмысленных “assert(true)”).


  6. Объекты с данными должны быть задекларированы на самом низком (из возможных) уровне области видимости.


  7. Возвращаемое значение не-void функции должно проверяться вызывающей функцией. Валидность параметров должна проверяться внутри каждой функции.


  8. Препроцессор можно использовать только для включения header-файлов и простых макро-определений. Token pasting, вариативные функции и рекурсивные макро вызовы запрещены. Использование условных директив компиляции нежелательно, но иногда неизбежно. Это означает, что только в редких случаях уместно использовать больше чем одно или два условия в директивах компиляции, даже в больших проектах.


  9. Использование указателей должно быть ограничено. Допустимо не больше одного уровня разыменования. Операторы разыменования не должны быть скрыты в макро определениях или внутри typedef. Указатели на функции запрещены.


  10. Весь код должен компилироваться при всех включенных warning'ах, на самых дотошных настройках компилятора с самого первого дня разработки. Весь код должен компилироваться с такими настройками без единого warning'а. Весь код должен проверяться каждый день (как минимум раз в день, но желательно чаще), с использованием лучшего из доступных на текущий день статического анализатора кода, и должен проходить анализ без единого warning'а.
Поделиться с друзьями
-->

Комментарии (116)


  1. dbelka
    13.06.2016 15:57
    -40

    Вот интересно, они(NASA) планируют использовать Rust? Вроде, язык хорошо подходит под их цели, множества ошибок помогает избежать.


    1. Denis_Minin
      13.06.2016 16:08
      +4

      Думаю они не изменят свои методы


    1. Ivan_83
      13.06.2016 16:19
      +60

      Вот заняться им больше нечем, кроме как переписывать весь код каждые х лет на новом модном языке.

      Не считая того, что ни один другой язык не имеет такого широкого охвата в плане того что на нём и ядра ОС пишут, и дрова, и программы.
      Си как был 40 лет назад, так и есть до сих пор, а учитывая правила описанные выше думаю у них очень незначительные проблемы с тем чтобы взять древний код и заюзать его.


      1. Halt
        13.06.2016 16:51
        -81

        Rust — не «еще один модный язык». Советую все же ознакомиться с основами языка, либо не демонстрировать свое невежество.


        1. khim
          13.06.2016 17:39
          +76

          А вам советую подумать о том, что когда мы говорим о проектах, которые занимают десятилетия (от момента запуска до точки назначения зачастую полёты годы занимают), то язык, первый релиз которого вышел чуть больше года назад — это просто несерьёзно. Вот пройдёт лет 10 — можно будет о чём-то говорить.


          1. CheatEx
            14.06.2016 12:33
            +2

            А на каком языке они по вашему писали в 70е?


            1. khim
              14.06.2016 13:15

              Там несколько языков было. Ассемблер, виртуальная машина от MIT и прочее, но, уж конечно, не С. С там появился, скорее всего, в 80е, примерно когда Space Shuttle начали разрабатывать.


              1. cadmi
                14.06.2016 15:01

                Кого начали? Когда начали?

                Space Shuttle полетел в 1981 году.


              1. CheatEx
                14.06.2016 17:47
                -2

                То есть шли на риск использовать технологию моложе 10 лет?


            1. webkumo
              14.06.2016 17:08

              Что-то железячное могли писать на Ada — с полгода назад на гиктаймсе статья пролетала, что в НАСА скоро останутся без последнего разработчика, способного что-либо сделать с Вояджером (кажется) — ибо док уже нет, спец остался один и тот имеет очень пенсионный возраст…


              1. locutus
                15.06.2016 09:17

                На Ada и сейчас пишут, пишут активно. И железное, и высокоуровневое. В NASA, ESA. Как у нас — не знаю, слишком мало информации у меня.


        1. Ivan_83
          13.06.2016 21:16
          +11

          Ок, пусть не модный, просто ещё один.

          Есть огромная вселенная языка Си: куча платформ, компиляторов, библиотек.
          Ещё куча всякого кода, примеров, проектов и тп.

          Есть остальные языки, которые часто заимствуют код из мира си, либо портируя либо подключая библиотеки.
          При этом они сами ничего полезного другим предоставить почти всегда не в силах: оно само не портируется в другие языки, подцепить в качестве библиотеки их как правило сложно/не возможно.

          Можно приводить сколько угодно любых других кроме Си в пример как более удобные, показывать их фичи, но всегда когда ни них пишешь ты рано или поздно (зависит от размеров проекта и что он делает) упираешься в то, что тебе нужно что то из мира Си.

          Си не универсальный, многие относительно простые вещи проще и быстрее писать на других языках.

          Вот они там у себя пишут что на нам можно писать дрова, ядра ОС и прочее.
          А что вообще на Rust написано?
          Как собрать RUST прогу под какой нибудь эльбрус? или ущербный контроллер?

          Тут вот написано где и в каком виде он есть: https://doc.rust-lang.org/book/getting-started.html
          Венда, мак и фря только х86.
          И что с этим делать? И главное зачем?

          Я уже молчу про то, что в нём вообще смысла нет, пока весь остальной код написан Си, та же операционка которую он дёргает.
          Подход НАСА с упрощением кода и максимальной диагностикой даст результат не хуже раста.


          1. tgz
            13.06.2016 23:30
            -17

            Сразу видно что вы ничего не знаете про rust. Он будет работать и на контроллере и на эльбрусе. А уж с интеграцией в си проекты вообще никаких проблем.
            К тому же никто и не собирается переписывать все что есть, речь про написание нового кода.


            1. Ivan_83
              14.06.2016 04:30
              +2

              Языков и раньше было не мало, а сейчас их стало много.
              Тратить на каждый новый язык время, изучая ради изучения смысла не вижу. А для большинства задач мне хватает Си, иногда перла, иногда пхп, иногда sh скриптов (у меня баша в системе нет).

              Разумеется, когда нибудь может быть будет работать где то ещё, но сейчас там очень скромный список архитектур.

              Ответа на простой вопрос: зачем писать на RUST, когда можно написать на… нет.
              Более того, даже ниша его применения не очень то очевидна.
              Проектов на нём тоже практически нет.

              Для академической среды или для не очень практических задач думаю оно вполне, а для серьёзных проектов лучше использовать более проверенные и предсказуемые (в плане развития и понимания что оно не помрёт к концу проекта/через 5 лет) языки.

              2 ctacka:
              Это да, но иногда проще взять другой язык и за пять минут натяпляпать там какой то рабочий прототип, в некоторых случаях этого более чем достаточно.
              В своё время мне надоело писать на Си UPnP сервер (логику), уж сильно много возни было с XML-SOAP и всякими там кодировками XML и я переписал на PHP, а для HTTP взял nginx. Этот проект на PHP мне проще писать/отлаживать, даже при почти полном отсутствии знания и понимания PHP.


              1. EviGL
                14.06.2016 18:48

                Не нужно переписывать/отказываться от старого кода, он без проблем подключается и применяется.

                Ответ на вопрос «зачем?» заключается в наличии гарантий безопасного управления памятью без оверхеда на это в рантайме. Именно это бич Си, именно это могло бы пригодиться в NASA для сверхнадёжных программ.
                Только не надо говорить, что это не проблема, это уже стоило человечеству очень многие миллионы долларов в ретроспективе.

                Но, естественно, нет смысла в NASA переключать сейчас разработку на RUST 1.0.сколько-то.alpha, надо дождаться стабильной стандартизированной проверенной версии, полностью на 100% отлаженного и доверенного компилятора и т.д.
                Рассматривать в перспективе есть смысл, чисто из-за этой фишки с памятью.


                1. Ivan_83
                  14.06.2016 20:27

                  Хз о каком биче вы говорите, у меня нет проблем с управлением памятью в Си.
                  Я тоже стараюсь по меньше выделять/освобождать во время работы, и по больше использовать либо статические либо выделенное в начале работы.

                  Опять же хз какое у них там железо, я слышал что у нас делают х одинаковых систем, которые считают одно и тоже, и результат большинства считается правильным.
                  Те там помимо программных ошибок бывают ещё аппаратные сюрпризы.

                  Опять же, специалистов наСильников найти проще, и это не меняется уже 2-3 десятка лет, а учитывая что на нём написаны ОС и вообще это почти стандарт де факто (в тех же RFC примеры или псевдокодом или Си) то ситуация не изменится и в будущем.

                  В этом плане американцы пошли проверенным путём: взяли и написали жёсткие стандарты как надо делать, а дальше или у тебя получается нормальный результат или тебя увольняют по не соответствию.

                  И мне вспоминается Visual Basic: на нём тоже было всё быстро и безопасно, и не считая того что он внезаптно почти умер, там всегда была проблема с вызовом WinAPI — нужно было руками писать декларации функций. Отдельно там были «приятные особенности» с выделение буферов, получением указателей и пр.
                  Когда в 2002 году перешёл на Си с ВБ прям почувствовал себя белым человеком, для которого всё уже сделано: и декларации функций и примеры кода…
                  Во всякие огороженные загончики я теперь не хочу, не стоит оно того, не стоит. ИМХО.

                  Никакой язык не гарантирует никаких сверхнадёжностей, его пишут такие же люди. А тем более для RUST нужна ещё операционка, сам то по себе работать не умеет.


                1. Mirn
                  14.06.2016 21:12

                  есть одна мааааленькая проблема:
                  для RUST наверняка нужен процессор с MMU (memory managment unit — блок переназначения виртуальной памяти в физическую).
                  А это и падение быстродействия, и требование наличия ОС, и в разы больше транзисторов и триггеров.
                  А всё это даёт дополнительные точки отказа, особенно в среде с повышенной радиацией, а по «праздникам» с зашкаливающей радиацией. Что есть не самая лучшая идея особенно ради просто моды.

                  Ладно, чёрт с ней с физикой и сложностью.
                  Мне вот интересно другое: есть ли методика безопасного кодинга, проверки и поиска ошибок для RUST выстраданная десятилетиями и миллиардами долларов убытков? Надёжные инструменты которые не дают незначительные ошибки раз в 100000 строк кода? Проверенные годами библиотеки? Надеюсь за фанатов что есть.


                  1. khim
                    15.06.2016 12:51

                    для RUST наверняка нужен процессор с MMU (memory managment unit — блок переназначения виртуальной памяти в физическую).
                    Нет, Rust'у нафиг не нужен MMU. И вообще он один из редких совеременных языков, способных на голом «железе» сидеть, без операционки, на микроконтроллере с ограниченной памятью.

                    Мне вот интересно другое: есть ли методика безопасного кодинга, проверки и поиска ошибок для RUST выстраданная десятилетиями и миллиардами долларов убытков?
                    Какие могут быть «выстраданные десятитителиями методики», если языку чуть больше года? Позаимствованные из других языков — да, есть, там много идей, «проверенных годами» идей, которые теоретически должны обеспечивать «безопасный кодинг», но как оно там на практике больших проектов в миллионы строк работать будет — ещё рано говорить.

                    Молодой Rust пока — это да, но задатки хорошие. Переводить на него всякие сильно критичные системы — я бы пока не стал. А вот какой-нибудь мелкий проект — уже вполне можно изображать…


            1. AccessGranted
              14.06.2016 13:49

              Первая стабильная версия Rust вышла лишь в том году а вы хотите чтобы на нем писалось ПО для космических кораблей? Ну ну. Может лет так через 10, если язык покажет свою живучесть и станет более или менее стабильным, будет смысл поговорить об этом.


          1. ctacka
            14.06.2016 00:37
            +8

            Си не универсальный

            Си как раз универсальный. На нем можно написать что угодно. И практически как угодно.


            1. Regis
              14.06.2016 02:04
              +4

              Скорее всего поразумевалось, что Си не очень подходит, когда нужно высокоуровое программирование. Просто не ээфективен за счет более низкой скорости разработки.


      1. eterevsky
        13.06.2016 18:09
        +4

        40 лет назад — это 76-й год. В то время в C был принят такой способ определения параметров у функции:


        int foo(a, p) 
            int a; 
            char *p; 
        { 
            return 0; 
        }

        Более-менее современный C — это ANSI C 89-го года или ISO C99.


        1. Chulup
          13.06.2016 18:15
          +15

          И кто мешает взять такой код и скомпилировать его сейчас? На любой платформе любой популярный компилятор его соберёт.


          1. laphroaig
            14.06.2016 11:26

            надо же собрался — никогда не задумывался об этом. жаль c++ не берет, а было бы удобно с трехэтажными шаблонизированными параметрами, что-то типа auto возвращаемого значения в c++11


          1. vlad72
            14.06.2016 15:18
            -1

            Бюджет.


          1. bigfatbrowncat
            15.06.2016 10:47

            Нарушите правило 10 :)

            Любой современный компилятор на такую архаичную запись выдаст предупреждение.


            1. kloppspb
              15.06.2016 12:47

              Любой это какой? gcc с ключами -pedantic -pedantic-errors -Wall, например, прекрасно это лопает, даже в принудительном режиме C99.


              1. khim
                15.06.2016 12:56

                И даже в C11. Потому что даже в C11 — это неотъемлемая часть языка C. Только в приложении «Future language directions» упоминается, что эта фича, в общем, довольно давно устарела и, возможно, когда-нибудь, от неё таки и откажутся. Может быть.


    1. eterevsky
      13.06.2016 18:12
      +5

      Я согласен с тем, что Rust хорошо сочетается с заявленными целями писать максимально безопасный код. Но если запретить динамическое выделение памяти, то его преимущества перед C радикально уменьшаются.


      Don't get me wrong. Я люблю Rust и надеюсь, что он получит большее распространение в обозримом будущем.


      1. Denis_Minin
        13.06.2016 18:14
        +4

        Но все же, нет особой необходимости менять на Rust.


        1. red75prim
          14.06.2016 15:18

          Для NASA может быть и нет. У них, надо полагать, все эти правила не только на бумажке написаны, но и проверяются статическими анализаторами и сотнями тестировщиков. А остальным, всё-таки, стоит задуматься.


  1. segment
    13.06.2016 16:03
    +1

    А чем указатели на функции плохи?


    1. khim
      13.06.2016 16:08

      Как вы собрались правило #2 будете проверять с указателями на функции?


      1. segment
        13.06.2016 16:14
        +1

        Ну у выбранной функции (стратегии), которая вызывается по указателю, может быть такая проверка.


        1. khim
          13.06.2016 16:41
          +1

          Это значит что нужно выходит за рамки C и расширять язык. Скорее всего они этим заниматься не хотят.


        1. interprise
          13.06.2016 17:24

          к чему эти уложения? чтобы сложнее проверять и легче допустить ошибку? а если марсоход встанет из-за этой ошибке. В их деле не стоит гнаться за скоростью написания кода


      1. Jef239
        14.06.2016 15:18

        ЛЕГКО. Вызов функции по указателю просто не влияет на циклы.

        Как пример использования. Есть функция — запаковать в пакет прикладного уровня и отправить. И у неё аргумент — функция отправки транспортного уровня. Транспортных подсистем — 3-4 штуки. Соответственно делаются обертки к функции запаковки — запаковать и отправить таким-то транспортом (или транспортом, указанным в конфиге).

        В итоге получается достаточно безопасная конструкция.

        Преимущество — память на прикладной конверт выделяется той функцией, которая знает, сколько памяти надо.

        Все это можно сделать через свитч, но оно менее расширяемо получится.


    1. dkukushkin
      13.06.2016 16:43

      Не ясно чем их заменить с C. Когда, к примеру, требуется вызов функции по системному таймеру, обычно адрес фукнции записывается в отведенную для вызовов таймера ячейку и при наступлении события таймер дергает функцию, адрес которой там указан. Чем заменить этот механизм?


      1. segment
        13.06.2016 16:58
        +1

        Вероятно они предполагают писать статичный код целиком, то есть с известными именами функций и ветвлениями. Для небольших проектов такое могу представить.


        1. Krypt
          13.06.2016 18:53

          Судя по правилам — они используют какой-то инструмент, перебирающий все доступные варианты ветвления (доказательство корректности программ?).


          1. Nepherhotep
            14.06.2016 10:54

            Я думаю, это обычный статический анализатор, и судя по тексту — общедоступный, а не какой-то свой.


            1. kloppspb
              14.06.2016 12:23

              Там упоминается MISRA, а не так уж много анализаторов, которые это умеют.


      1. khim
        13.06.2016 17:42
        +1

        А зачем этот механизм менять? Я так понимаю что контантные указатели на функции, обусловленные железом, не запрещены. Кроме того я уверен, что у них есть много высокоуровневого кода, который не влияет на выживаемость железяки и для него требования не такие жёсткие…


      1. gsaw
        13.06.2016 23:26
        +3

        Перевод неверный или исходник перевода сам корявый. По той же ссылке на оригинальные правила:

        *29 Do not use non-constant function pointers.
        30 Do not cast function pointers into other types.


        1. kloppspb
          13.06.2016 23:44
          +1

          Кстати, да. И в том же документе как раз много чего про использование указателей на функции, откуда взялся запрет — непонятно. Ну разве что в таком контексте: «IPC messages should then contain only data, preferably no data pointers, and
          never any function pointers». Что-то не так тут с переводом, одного беглого взгляда на огигинальный документ достаточно чтобы это понять.


    1. gleb_l
      13.06.2016 19:33
      +6

      Вычислимые адреса переходов/вызовов чреваты непредсказуемостью поведения программы так сказать второго рода, когда некорректные входные данные способны не только породить некорректные выходные (GIGO), но и разрушить логику работы конечного автомата — а это может привести к полному краху системы.

      В случае же, когда все переходы определены в статике, бортовая вычислительная система представляет собой конечный автомат с памятью, который всегда можно перезапустить очисткой (приведением) набора входных данных к валидному состоянию.

      Все вышесказанное, конечно, имеет смысл либо для гарвардских архитектур, либо для систем с защитой памяти (не обязательно имеющих виртуальную!)

      Динамическая память — туда же. При отсутствии механизма виртуальной памяти невозможно гарантировать 100% корректную работу системы в течение бесконечного времени при динамическом выделении/освобождении памяти даже при любом априори предустановленном суммарном лимите для каждого процесса из-за фрагментации. При наличии механизма виртуальной памяти такое становится возможным — но взамен мы получаем лишь статистическую верхнюю границу времени отклика на выделение блока памяти


    1. serbod
      14.06.2016 13:44

      Невозможно (или слишком сложно) проверить корректность перехода по произвольному адресу, особенно когда данные и код в одном сегменте. При малейшей ошибке последствия очень плохие. Так что переход строго по константам со строгой типизацией. Если нужно разнообразие, то достаточно case.


  1. iSlava
    13.06.2016 17:16
    +28

    Вот где реально интересно было бы посмотреть на результаты прогона PVS-Studio :)


    1. equeim
      13.06.2016 19:10
      +4

    1. avas
      13.06.2016 21:05
      +3

      Некоторые вещи в статике не протестируешь :)

      У Европейского космического агенства была крутая новая ракета Cluster. Там дажу было горячее резервирование системы управления ракетой — но и оно не спасло от необработанного exception. Обе ноды синхронно упали :)

      Эта неудача входит в список самых дорогих ошибок программистов


    1. Rastishka
      13.06.2016 21:20
      +8

      Вот где реально интересно было бы посмотреть на результаты прогона PVS-Studio :)


      Кстати да. Призываю в тред Andrey2008


  1. eterevsky
    13.06.2016 17:56
    +13

    «The assertion density of the code should average to a minimum of two assertions per function.»

    В среднем должно быть не менее двух ассершнов на функцию.


    1. DaylightIsBurning
      13.06.2016 20:13
      +5

      Дополню.

      The return value of non-void functions must be checked by each calling function

      Возвращаемое значение не-void фунций должно проверяться вызывающей функцией.

      Примечание: идея такая, что функция не должна доверять ничему вообще, кроме себя самой, ни переданным аргументам, ни значениям полученным из других функций.


    1. SkidanovAlex
      13.06.2016 21:21
      +4

      А я думаю — как assert(true) может помочь обойти правило не более двух ассертов.
      Так намного больше смысла.


  1. Sayonji
    13.06.2016 18:12
    -11

    «10 правил, которые позволяют NASA писать миллионы строк кода с минимальными ошибками» —> «10 правил, которые заставляют NASA писать миллионы строк кода».


    1. amarao
      13.06.2016 18:20
      +42

      Крутая сатира. Поддел так поддел. В то время, когда любой программист на php/node может написать код для кьюриосити за два вечера и пару десятков тестовых запусков, nasa тратит десять лет на один-единственный запуск. Вот лузеры же.


    1. VeZooViY
      14.06.2016 13:34
      +2

      Да… Как же плохо что программистов заставляют писать хороший код


  1. zim32
    13.06.2016 18:54
    -4

    Слышал что UI у spacex на HTML пишут. Может это все доказывает, что неважно какой язык, главное какие программисты?


    1. herr_kaizer
      13.06.2016 19:05
      +13

      А какая разница, на чем интерфейс отрисовывать?


      1. ctacka
        14.06.2016 00:41
        +24

        Ну летите вы к земле, а на лендинг странице кнопочка приземления уехала.


        1. turbo_exe
          14.06.2016 13:08
          +1

          насколько я понимаю, все важные кнопочки приземления выполнены в виде хардварных тумблеров. а интерфейс на хтмл — для разного рода научных программ и прочих тестов в невесомости.


          1. petrovnn
            14.06.2016 17:58
            +1

            скорее продублированы. Потому что экран может сломаться


  1. Veliant
    13.06.2016 19:13
    +1

    Собственно, все самое интересное находится в документах MISRA C с примерами как делать хорошо и как плохо


  1. Wesha
    13.06.2016 19:26
    +11

    Великолепные правила для неспешной разработки надёжных систем. Ужасные правила для быстрой разработки ненадёжных систем.


    image


  1. Unrul
    13.06.2016 20:05
    +15

    0. Иметь бюджет $1,000 на одну строку кода.


    1. DaylightIsBurning
      13.06.2016 20:14
      +3

      зато какая приятная работа для души, прямо строительство идеального механизма.


  1. mbait
    13.06.2016 21:04

    У всех циклов должен быть предел. Проверяющая программа должна иметь возможность легко доказать, что определенное количество итераций не может быть превышено. Если предел невозможно доказать статически, то правило считается нарушенным.


    Хотелось бы узнать больше подробностей, ибо известно, что в общем случае проблема останова не решаема.


    1. khim
      13.06.2016 21:15
      +2

      В общем случае рассматривается общая программа. Ваш КО.

      Какой-нибудь BPF в проблему останова ни разу не упирается, почему NASA должна?


      1. mbait
        13.06.2016 21:21

        Какой-нибудь BPF в проблему останова ни разу не упирается, почему NASA должна?


        Странная логика. Я не говорил, что "NASA должна".
        В документе JPL есть много правил, но пользователь Реддита сделал выжимку десяти главных принципов.


        то есть скорее всего есть какое-то формальное доказательство, что следуя всех их правилам, можно гарантировать, что программа завершиться за конечное число шагов. Я лишь хотел подчеркнуть, что вне контекста правило №2 непонятно совсем. Более того, непонятно, как его выполнять.


        1. vadimr
          13.06.2016 21:33
          +1

          Правило совершенно понятно вне контекста, и, на мой взгляд абсолютно ясно, как его выполнять: не надо писать циклов неопределённой продолжительности. Наверняка, в других документах вдобавок прямо сказано, что любая процедура в прошивке космического корабля обязана гарантированно возвращать управление, скажем, не более чем через 100 мс.

          На практике это означает, что сложные алгоритмы должны быть ограничены не только достижением результата, но и заданным количеством итераций до неуспеха.


    1. vadimr
      13.06.2016 21:20
      +2

      Так они не программируют общий случай алгоритмов. Задача, вычислительная сложность которой неизвестна, не должна программироваться в бортовой компьютер.


    1. locutus
      14.06.2016 16:43
      +2

      Математическая поправка: машину Тьюринга нельзя построить, так как она сама по себе бесконечная (это требуется для доказательства некоторых теорем). Для алгоритма, выполняемого на конечной системе, всегда можно решить проблему остановки, так как она является тривиальной для такой системы. В этом случае машина Тьюринга будет эквивалентна некоторому большому конечному автомату (что, кстати, косвенно обсуждается в предложенных правилах от JPL), для которых существуют хорошие алгоритмы проверки формальной корректности.

      Вот пример

      http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20030017985.pdf

      Да и вот тут много чего интересного есть

      http://lars-lab.jpl.nasa.gov/


      1. pak-nikolai
        15.06.2016 00:31

        абсолютно верно, конечные автоматы и ватч доги в каждой точке где возможно зацикливание резко сокращают неопределенность системы


    1. geekmetwice
      16.06.2016 21:02
      -1

      Просто ребята умно перефразировали «никогда не пишите while(true)» :)


  1. gandjustas
    13.06.2016 23:41
    +5

    Что-то я сомневаюсь, что дело в 10 правилах.


    Дело скорее:
    В детальном прописывании спецификаций и их низменности на протяжении всего времени разработки
    В статическом анализе кода
    В обязательном code review
    В огромных затратах на тестирование


    В таких условиях неважно на каком языке ты пишешь и какого стиля придерживается.


    Очевидно, что правила про отсутствие динамической памяти, рекурсии, ограниченность циклов и отсутствие указателей на функции появились чтобы упростить работу статическому анализатору


    1. kloppspb
      13.06.2016 23:53
      +3

      Да нет там запрета указателей на функции. И вообще, как умудрились при переводе 31 правило (а то и 33) упаковать в 10, да ещё и переврать — непонятно.


    1. serbod
      14.06.2016 14:06

      Не-не-не, это как правила дорожного движения. Если их не соблюдать, то никакие навигаторы и техосмотры не помогут.

      Анализатору и компилятору пофиг, он машина. Запятые, скобки на месте? Годен! А вот живому человеку сложно читать и отлаживать код, в котором много условностей и контекстов. Забудешь вернуть память или выйти из рекурсии — и привет. Проще это болото обойти, потратишь лишний час разработки, но сэкономишь недели и месяцы отладки.


      1. gandjustas
        14.06.2016 14:49

        На моей практике наблюдалось явление: чем больше код проверяется, тем внимательнее пишут программисты. Это касается именно проверки исходников, а не тестирования.


        Добавляя статические проверки и code review к процессу можно сразу получить на порядок лучшее качество.
        Видимо срабатывает логика: код будут проверять — надо писать хорошо. И обратная логика тоже работает прекрасно. Если никто по факту не код не проверяет, то пишут его абы-как.


        1. serbod
          14.06.2016 17:47

          Тут дело не в качестве, а в сознательном отказе от использования вполне корректных и полезных, но потенциально опасных возможностей.


          1. gandjustas
            14.06.2016 17:59

            Еще раз повторю тезис.


            Дело вовсе не в отказе, в большем контроле что пишут программисты.
            Сколько бы ограничений вы не вводили — это не поможет не писать говнокод, если программист захочет.


            А если заранее спланировать что напишет программист, проверить статическим анализатором, прогнать через code review минимум двух человек и потратить в два раза больше времени на тестирование, то по сути пофиг какие там ограничения вы себе навводили.


            1. serbod
              14.06.2016 21:12
              +2

              Говнокод писать можно и нужно. Потому что у кода, от которого зависит сохранность имущества и жизни людей нет оценки говно-охуенно. Он либо работает как надо в любой ситуации, либо не работает и происходят плохие вещи. А как оно там внутри, грамотно или говняно — никого совершенно не интересует.

              Вот, к примеру, 5-й пункт про ассерты. Они никак не влияют на функционал, это тесты. Они жрут такты при каждом вызове функции, особенно в циклах. Но это самые точные тесты, которые легко поддерживать актуальными. А еще они, в отличии от юнит-тестов, способны сохранить программу «на плаву» уже в полете, когда невозможное случилось и без отладчика точно указать на проблемное место.

              или 7-й пункт, проверять все, что пришло снаружи или изнутри, будь то параметры или результаты. Например, банальная функция инкремента. Знаете, что будет с байтом 0xFF, если его увеличить на единицу? Будет ноль. А это во некоторых случаях может привести к беде. Поэтому в самом инкременте должен быть ассерт на переполнение, и на выходе нужно проверить, что результат инкремента является допустимым. Глупости? Отнюдь! Реальный пример — при формировании более светлого цвета фона для выделения важного сообщения происходило переполнение байтов RGB и получался черный текст на черном фоне. 15 лет работало нормально, а потом в системе поменяли цвет фона на чисто белый (0xFFFFFF) и получился эпический фейл. Вот каким анализатором или code review можно было такое предусмотреть?

              С указателями самая больная тема. Нетипизированные указатели это граната с обезьяной. Проще сразу отказаться, чем потом страдать. Память из кучи это по сути своп — там и фрагментация свободного места, и конфликты, и утечки. Когда используется всего 5-10% от свободного объема, то это незаметно. А когда памяти впритык, то начинает всякая фигня происходить. Может всплыть через месяц, через год. Поэтому тестирование таких вещей идет неделями с 10-кратной нагрузкой на пределе возможностей железа.


    1. hopungo
      14.06.2016 14:22

      Низменность детально прописанных спецификаций — сильно!

      Высокодуховный код безбажен по определению :)


    1. alygin
      14.06.2016 19:01
      +2

      Вспомнилась интересная (даже спустя 20 лет после написания) статья о том, как устроена работа в одной из групп, разрабатывающей ПО для бортовых систем шаттлов: They Write The Right Stuff. В числе прочего, в ней есть интересные данные по доле ошибок в расчете на количество строк кода, по объему и качеству спецификаций в проекте, а также по размеру его бюджета. Пара цитат, передающих общую идею:

      «Our requirements are almost pseudo-code,» says William R. Pruett, who manages the software project for NASA. «They say, you must do exactly this, do it exactly this way, given this condition and this circumstance.»

      The group has one customer, a smart one. And money is not the critical constraint.

      Ничуть не принижая способности их разработчиков, следует отметить, что с организационной точки зрения условия им созданы очень близкие к идеальным.


      1. serbod
        14.06.2016 22:17
        +1

        У меня тоже задачи однозначны, просты и понятны, любой студент справится. Вся проблема в том, что в системе должно быть 0 (ноль) ошибок на 560 000 строк кода. И работать она должна несмотря ни на какие сбои и неполадки железа и софта, ошибки персонала. Но, в отличие от НАСА, денег и ресурсов минимум, а ответственность не меньше.

        Скрытый текст
        Пульт центрального наблюдения для ohrana.gov.by Немного почитать — www.serbod.com/projects-taken/rf-link


        1. alygin
          15.06.2016 09:00

          в системе должно быть 0 (ноль) ошибок на 560 000 строк кода. И работать она должна несмотря ни на какие сбои и неполадки железа и софта, ошибки персонала.

          Стандартное в наши дни требование практически от любого заказчика в корпоративном сегменте :)

          А если серьезно, то некоторые из описанных задач, конечно, далеко не для любого студента. Поделитесь, какими способами пытаетесь поддерживать низкое количество ошибок в системе, в которой есть сторонние компоненты, включая целые базы данных, а также
          некоторые драйвера устаревших приборов невозможно протестировать и отладить, потому что они сложные, к ним нет ни документации, ни тестовых приборов

          ? Используете похожие на NASA'вские правила при разработке? Требуете как и они от заказчика подробнейшие спецификации? Содержите ли сильнейший отдел тестировщиков? Пытаетесь вытягивать на личных профессиональных качествах старших разработчиков?


          1. serbod
            15.06.2016 10:34

            Ошибки в коде для системы вовсе не повод, чтобы не работать. =) Система разбита на множество компонентов, и если ломается один, то остальные либо ждут его восстановления, либо переключаются на запасной. Главное, чтобы поставленные задачи решались в пределах допустимых отклонений от нормы. И не допускать повторения ошибок.

            Лично я правила использую. Но не всегда им строго следую. Требовать спецификации можно, но не всегда успешно. И в них могут быть ошибки, устаревшие сведения. Только практика, испытания и анализ дают достоверный результат. Тестировщики есть, и очень дотошные. Личные качества вообще самое главное, ведь технику делают люди.


            1. Wesha
              15.06.2016 18:15

              Требовать спецификации можно, но не всегда успешно. И в них могут быть ошибки, устаревшие сведения.


              "В 1962 году головной по командному модулю кораблей серии «Аполлон» фирмой «North American Aviation» (позже — «North American Rockwell») при изменении технического задания на изготовление кислородных баллонов для двигательных отсеков, выданного субподрядчику фирме «Beech Aircraft», не была предусмотрена модификация термостатов, изначально рассчитанных на напряжение 28 В, под стандартное для наземного оборудования стартового комплекса напряжение в 65 В. Это несоответствие не было замечено ни специалистами обеих фирм, ни НАСА. [...] проведённые в ходе расследования эксперименты показали, что эти выключатели, рассчитаные на питание в 28 вольт постоянного тока от батарей служебного модуля, не размыкались должным образом при работе под напряжением 65 В, подаваемым со стартового комплекса в ходе выпаривания кислорода."


  1. IvanPonomarev
    13.06.2016 23:58

    В NASA JPL большие молодцы. Но поучительно помнить, что все эти правила не помешали едва не угробить марсоходы… из-за банальной вещи — из-за того, что на Земле никто не протестировал случай, когда марсоход наделал много фотографий и его FLASH-память почти заполнилась.


    1. xeonz
      14.06.2016 13:34

      Память закончилась не из за фотографий, а из за ошибки реализации файловой системы, которая приводила к неполному освобождению места при удалении файлов/папок.


  1. valeriyk
    14.06.2016 00:50

    Читал ровно эти самые десять правил лет 7 назад в каком-то англоязычном журнале, написано было один в один


  1. f3ath
    14.06.2016 09:52

    Правило #7, кажется, называется defensive programming и считается немного противоречивым.


    1. khim
      14.06.2016 13:36

      Для NASA — в самый раз. Подобный подход приводит к тому, что ошибки имееют меньше шансов привести к провалу миссии, но увеличивают вероятность того, что останется ошибка, которую сможет использовать «враг». Так как бортовому компьютеру с атаками на безопасность сталкиваться не приходится — то выбор очевиден.


  1. moveax3
    14.06.2016 09:58
    +3

    > Объекты с данными должны быть задекларированы на самом низком (из возможных) уровне области видимости.

    А самая низкая, это где? Самая глобальная или самая локальная?


    1. sigizmund
      14.06.2016 10:44
      +1

      Самая локальная. Ну это более или менее здравый смысл — если переменная нужна только в конкретном блоке кода, делать ее глобальной это заранее создавать себе крупные проблемы.


  1. Shamov
    14.06.2016 11:19
    +4

    Эти правила просто напросто вытекают из ограничений той самой «проверяющей программы», которая вскользь упоминается в некоторых из них. Само собой, что если у тебя такая программа есть, то ты вынужден писать код таким образом, чтобы она могла его проверять. Но использовать эти правила, не имея в своём распоряжении проверяющей программы, — это то же самое, что и не иметь автомобиля, но при этом передвигаться в сидячей позе, выставив вперёд руки, которые, как бы, держат руль, и издавая ртом звуки, имитирующие работу двигателя.


    1. serbod
      14.06.2016 14:23
      +2

      Нет, к этим правилам рано или поздно приходит любой разработчик масштабной системы с высокими требованиями.

      Когда я впервые столкнулся с этим при разработке под игровые приставки (на картриджах), то тоже был несколько скептичен. Зачем же себя так ограничивать? Но позже, при отладке совершенно непонятных рандомных глюков, постепенно пришлось отбрасывать весь синтаксический сахар, регламентировать работу с памятью и вставлять сотни ассертов. В итоге ошибка находилась, они могла быть совсем пустяковой, и не влиять на работу сразу, а вызывать сбой где-то в другом месте программы.

      Я бы еще добавил, что очень важна строгая типизация и однозначно говорящие имена функций и переменных. Сколько угодно случаев, когда тупиковая ситуация переставала быть тупиковой, когда вещи называли своими именами.


      1. hopungo
        14.06.2016 14:34
        -1

        Случаем, не так сделали?

        #define TRUE 1
        #define FALSE 0
        typedef int BOOL;
        
        // где-то в коде
        BOOL a;
        // повсеместно заместо
        // a = TRUE;
        a += 1;
        


        1. serbod
          14.06.2016 15:18

          У вас BOOL это синоним целого числа. В принципе, иногда допустимо, но тогда это iBool_t

          enum Boolean { FALSE = 0, TRUE = 1 }; // ибо нефиг!


      1. Shamov
        14.06.2016 15:01
        +1

        И объясняется это почти тем же самым. При разработке масштабных систем обычно происходит ревью кода другими разработчиками. Это бюджетный аналог той самой «проверяющей программы», использующий ручной труд вместо автоматизации. И, естественно, у этих разработчиков тоже есть свои ограничения. Некоторые из них плохо понимают хитроумные макросы. Поэтому их нельзя использовать. Некоторые другие вообще не знают всей кодовой базы в целом, и поэтому им не нравится код с исключениями, так как они сходу не могут понять, где и как исключение будет обработано. Им хочется смотреть в код только там, где были сделаны изменения, и чтобы сразу всё было понятно. Третьи разработчики плохо понимают С++-шаблоны, и поэтому их разрешено использовать только самым простым и очевидным способом. Так постепенно шаг за шагом формируются все эти правила. И, наверное, это правильно. Я собственно не возражаю. Единственное, меня смущает то, что этим правилам обычно приписывается некая универсальность. Якобы они имеют смысл не только в какой-то конкретной команде разработчиков, а вообще все должны их придерживаться. Типа, так код становится «более читаемым». Хочется спросить: «Читаемым кем?» А если код, например, ревьювят Александреску и разработчики boost? Почему бы не использовать шаблоны более агрессивно? Или если код ревьювит хардкорный сишник с 30-летним стажем? Почему бы не использовать сложные замороченные макросы? Ему будет приятно видеть, что кто-то ещё знает толк в извращениях. В общем, суть в том, что правила написания кода следуют непосредственно из ограничений проверяющей системы. Не важно, ручная она или автоматическая. Эти ограничения следует чётко осознавать, и тогда правила не будут выглядеть нелепо.


        1. serbod
          14.06.2016 15:32
          +3

          Разработчики становятся «некоторыми другими» даже после больничного или отпуска. А еще есть задачи, где абсолютный приоритет простоты и надежности над красотой и крутостью. Когда функция из десятка строк, (где 7 строк это проверки и обертки) лучше одной inline строки или макроса. Наважно, что в эту функцию попадет — корректное значение, некорректное или бозон Хиггса — она корректно отработает.


          1. Shamov
            14.06.2016 15:44
            +1

            Простота и надёжность не противопоставлены красоте и крутости.


            1. gleb_l
              15.06.2016 00:29

              Это противоречие между ученым и инженером. Первый — несравним со вторым по красоте решений и крутости, но — немасштабируем (вернее, негарантированно воспроизводим в любой внешней среде, создаваемой государством).
              Вторые — гарантированно воспроизводимы на уровне, скажем, не ниже четверки по профильному предмету при применении обучающего курса Х в течение Y лет.
              Как и любая индустрия, космическая промышленность нуждается в определенном количестве специалистов, способных решать задачи определенного класса с качеством не ниже определенного. Дальше — теория вероятности — чем выше требования к качеству — тем меньшее количество людей статистически годны для решения задач — а значит, а) процесс становится более персонозависимым, и б) прогноз выхода специалистов нужного уровня — недостоверным
              Это допустимо (и усиленно используется!) в гуманитарной области (поэты, художники итд) — но совершенно недопустимо в инженерии. Поэтому вводятся искусственные ограничения, повышающие надежность за счет снижения разрешенного множества выразительных средств — этими мерами процесс выводится из зоны персонозависимости. Снижение эффективности кода и/или красоты решения в данном случае не играют никакой роли (так как либо легко парируются техническими средствами, либо не являются метрикой качества/престижа разработчика вообще)

              Уместна аналогия с переходом от аналогового к цифровому аудио — да, аналоговая запись единственна и неповторима — но она и немасштабируема! Сознательное же внесение искажений в идеал (квантование) позволяет сделать предсказуемой любую ее копию.


              1. Shamov
                15.06.2016 06:32

                Параллель, проведённая между инженерами и художниками, несколько смазывается тем, что художники и поэты в гораздо большей степени, чем инженеры, ограничивают сами себя в допустимых выразительных средствах. Причём делают они это специально для того, чтобы показать свою крутость. У художников это выглядит как кубизм и супрематизм. А поэтов ограничивает необходимость соблюдать ритм и подбирать рифму. У Дэна Симмонса в Гиперионе был забавный персонаж — величайший поэт в галактике. После травмы головы у него что-то поломалось в мозгу и он мог использовать в своей речи только девять матерных слов. Много лет он был вынужден общаться, используя столь ограниченный набор слов. Но зато когда проблема исчезла и появилась возможность использовать любые слова, он с легкостью стал поэтом настолько крутым, что ему не было равных во всей галактике.


  1. nzeemin
    14.06.2016 11:46
    +1

    По репозиториям NASA видно, что далеко не только C/C++ используется — Phyton, Ruby, JavaScript, Java, FORTRAN и даже плагин на VB.NET к Excel.


    1. khim
      14.06.2016 13:16
      +1

      Это всё не в боротовом компьютере.


      1. coolspot
        15.06.2016 01:14

        VB.NET Excel в бортовом компьютере было бы очень забавно. =)


  1. olekl
    14.06.2016 13:59

    Интересно, а почему они более серьезные метрики кроме количества строк и ветвлений не вынесли в правила? Статический анализатор включили в правила, а метрики — нет.


  1. zuzuza
    14.06.2016 15:17
    -4

    Правило 0. Не использовать javascript никогда вообще никогда.


  1. supasupa
    14.06.2016 15:19

    > Возвращаемое значение не-void функции должно проверяться вызываемой функцией.

    вызывающей функцией

    Rule 14 (checking return values)
    The return value of non-void functions shall be checked or used by each
    calling function, or explicitly cast to (void) if irrelevant.


    1. freetonik
      15.06.2016 12:20
      +1

      В черновиках была правильная версия, в чистовик попала неправильная :-( Спасибо, исправил.


  1. Boletus
    14.06.2016 15:25
    +4

    Эти десять правил не «пользователь Реддита» выжал из JPL. Их сформулировал Gerard Holzmann с коллективом в документе «The Power of 10: Rules for Developing Safety-Critical Code» в 2006 году, как раз для JPL. Они вошли в основу нынешнего стандарта, наряду с MISRA C и другими дополнениями.

    Копия статьи: web.eecs.umich.edu/~imarkov/10rules.pdf
    Статья в Википедии: en.wikipedia.org/wiki/The_Power_of_10:_Rules_for_Developing_Safety-Critical_Code


    1. freetonik
      15.06.2016 12:19

      Большое спасибо, исправил пост и добавил вашу поправку.


  1. mafia8
    15.06.2016 21:10

    Бумага, толщина: 500 листов — примерно 50 мм, 1 лист — 0,1 мм. Пусть высота 1,7 м = 1700 мм. 1700/0,1 = 17000 листов. И когда она успела столько набрать?


    1. vadimr
      15.06.2016 21:38

      Миллион строк – в самый раз за несколько лет. Особенно если на ассемблере.


  1. csbubbles
    15.06.2016 22:40

    Там в документе всего 31 правило, кстати. Можно было бы их все перевести, остальные там тоже все make sense, десять представленные ничем особым не выделяются от общего списка. И я бы не стал называть их «жесткими». Правила, как правила. В одной организации одни, в других другие. Главное, хорошо, что они есть в принципе, и еще лучше, если им следуют.

    Я думаю, что это все-таки не какие-то определенные правила «позволяют NASA писать миллионы строк кода с минимальными ошибками», а ответственность людей, которые там работают. Наличие правил и их теоретическая осмысленность не гарантируют того, что им будут следовать или применять так, как предполагалось. Скорее все-таки, это в NASA это общее понимание потенциальных последствий даже, казалось бы, маленьких ошибок, заставляет людей намного более ответственно относиться к тому, что они производят, чем в большинстве ПО общего потребления, где факапы не столько значимы.


  1. 2xMax
    15.06.2016 23:12

    не могли бы вы дать пруф на «миллионы строк кода»? В оригинале есть только упоминание про «hundreds of thousands of lines», что как бы на порядок меньше:)