Файл: Технол_разраб_прогр_обесп_Гагарина_Кокарева.doc

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 20.11.2019

Просмотров: 13456

Скачиваний: 411

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

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

Хотя эту статистику тяжело применить на практике, но одно исследование показало, что оптимальное число пустых строк в программе составляет от 8 до 16 %. Если оно больше 16 %, то время, затрачиваемое на отладку, заметно увеличивается.

Отступы. Применяйте отступы для демонстрации логической структуры программы. Как правило, операторы выделяются отступами, когда они следуют после некоторого выражения, от которого они логически зависят. Оптимальными являются отступы из 2—4 пробелов [35].


Скобки

Используйте скобки чаще, чем вам это кажется необходимым. Применяйте скобки для разъяснения выражений, состоящих из двух и более членов. Возможно, в скобках нет нужды, но они добавляют ясности и ничего вам не стоят. Например, скажите, как вычисляется следующее выражение?

Вариант на С++: 12 + 4% 3 * 7 / 8.

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



5.10. Надежность программного обеспечения


Это комплексное свойство включает две составляющие [3]:

  • безошибочность (correctness) — соответствие спецификации;

  • устойчивость (robustness) или отказоустойчивость (fault-tolerance) — способность продолжать правильно работать после отказов.

Улучшение первого качества достигается хорошей технологией, предупреждающей ошибки (fault-avoidance). Однако 100%-ное отсутствие ошибок недостижимо. Устойчивость должна быть относительно любых видов отказов, для ее поддержания создаются специальные программно-аппаратные средства.

Отказ (failure) по ГОСТу — нарушение работоспособности изделия и его соответствия требованиям технической документации (рис. 5.3). Применительно к программам (стандарт ШЕЕ/ ANSI) отказ есть неспособность функциональной единицы системы, зависящей от программы, выполнять требуемую функцию в заданных пределах.

Повреждение Отказ

Восстановление

Рис. 5.3. Диаграмма состояний и переходов при отказе

Классификация причин отказов:

1. Физические дефекты:

  • внутренние (старение, износ);

  • внешние воздействия (радиация, высокая температура).

2. Ошибки людей:

  • ошибки эксплуатации или взаимодействия;

  • проектные ошибки.

Повреждение (Fault) — неисправность уже появилась, но еще не проявилась вовне. Например, отказала ячейка памяти, но из нее еще не читали или в нее еще не писали. Отказ компонента — это повреждение системы. Время нахождения в неисправном, но работоспособном состоянии называется латентным (скрытым) периодом отказа.

Восстановление (Recovery) — возврат в исправное состояние путем:

а) ручного ремонта, замены, исправления;

б) автоматически — задействованием резервов;

в) самопроизвольно, обычно быстро.


В случае в) отказ называется сбоем, т. е. сбой — это кратковременный самовосстанавливающийся отказ. Остальные отказы называются устойчивыми (по умолчанию отказ устойчивый). В электронной аппаратуре сбои происходят на порядок чаще устойчивых. Их причины — флюктуации питания, ситуации «гонок» сигналов, альфа-частицы и др. В программах аналогично сбоям ведут себя времязависимые ошибки — их иногда называют «мерцающими» (blinking bugs).

Типичный пример восстановления — с помощью резервной (backup) копии данных. Если выполнить восстановление в латентном периоде отказа, то он никогда не проявится вовне — в этом состоит идеальная отказоустойчивость.


5.10.1. Количественные характеристики надежности программ


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

«Внутренняя» характеристика надежности — количество оставшихся ошибок в программе — интересна больше разработчикам, чем потребителям. Для последних важны характеристики, традиционные для теории надежности, основанные на предположении о стохастическом (случайном во времени) процессе возникновения отказов: среднее время безотказной работы (MTBFMean Time Between Failures) и коэффициент готовности. Третья характеристика, взаимосвязанная с первой, — интенсивность отказов среднее их количество в единицу времени.

В предположении простейшего потока отказов (отказы независимы, редки и их вероятность неизменна во времени) P(t) вероятность безотказной работы за время t — подчиняется закону Пуассона (экспоненциальному распределению вероятностей):

ДО = е*,

где X интенсивность отказов (обычно в 1/час).

Его первый момент — математическое ожидание М(Р) и есть MTBF = 1/Х.

В табл. 5.4 приведены средние значения MTBF для устойчивых отказов.

Рис. 5.4. Зависимость потока отказов от времени

Таким образом, программы вносят наибольший вклад в (не)надежность современных вычислительных систем. Между тем существуют столь ответственные (mission-critical) приложения, где требуется очень малая вероятность отказов. Например, для бортовой системы управления космическим зондом требуется А.= 10"9, чтобы вероятность устойчивого отказа в первые 10 лет работы была не более 10"4 (или вероятность безотказной работы 0,9999), что означает MTBF= 100 тысяч лет!

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

Многие программные продукты (ПП) имеют аналогичный характер изменения надежности: А — период начальной экс-

в

Inf

Рис. 5.5. Типичное изменение X электронной аппаратуры во времени: А — период приработки («выжигание» дефектов); В — полезная жизнь; С — старение, износ

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

Если отказ все же произошел, время восстановления должно быть минимальным. Это характеризуется показателем ремонтопригодности коэффициента готовности (availability):

к=(Т-Тпр)/Т,

где Т общее время работы, Тпр — время простоя из-за восстановлений. В ответственных системах требуется, чтобы значение к почти не отличалось от 1: для цифровых АТС — 2 часа простоя суммарно за 15 лет; для системы управления воздушным движением — 3 сек за год!


5.10.2. Методы оценки и измерения характеристик надежности


«Внутренняя» характеристика — число оставшихся ошибок — абсолютное или относительное. Считается, что в хорошо отлаженной программе — не более одной ошибки на 1 тыс. строк исходного кода. Для ответственных применений требуется то же на 10 тыс. строк и больше. Например, для СОИ этот показатель был задан как не более 2,5 ошибки на 105 операторов языка Ада.

Как проверить соответствие ПП таким требованиям?

  1. Метод Миллса (IBM, 1972 г.) использует прием биологов, метод «меченых рыб». Группа тестирования засоряет программу искусственно ошибками и, продолжая тестирование, анализирует долю внесенных ошибок среди обнаруженных.

  2. Пусть внесено s ошибок, обнаружено п собственных и v внесенных ошибок. Предполагая, что вероятность обнаружения внесенных и собственных ошибок одинакова, получим первоначальное количество оставшихся ошибок N=sn/v. Проверка гипотезы: осталось меньше к собственных ошибок. Вносится еще s ошибок и тестирование продолжается, пока все они не обнаружены. Пусть к этому моменту найдено п собственных. Тогда уровень значимости гипотезы (т. е. вероятность того, что она истинна, равна 1 при п > к и s/(s + к + 1) при п < к.

  3. В методе Руднера (1977 г.) тестирование осуществляется двумя независимыми группами тестеров параллельно, которые обнаруживают тх и т2 ошибок соответственно, из которых т]2 ошибок — общие. Используя гипергеометрическое распределение, методом максимального правдоподобия может быть найдена оценка числа первоначально содержавшихся в программе ошибок N= mlm2/ml2.

4. Фирма Motorola использует в качестве меры надежности своих ГШ (управляющих программ реального времени) среднее число ошибок на 1000 строк исходного кода. Она разработала метод оценки времени тестирования без отказов (zero-failure method), подтверждающего заданную надежность. Метод основан на известной форме зависимости:

T=f(N, п, 0,

где Т — время окончательного тестирования — испытаний без отказов; TV — допустимое число ошибок в коде; п общее число ошибок, обнаруженных в ходе тестирования; / — время, потраченное на выявление всех п ошибок.

Если за время Т ни одного отказа не произошло, считается, что в программе не более N ошибок и тестирование можно закончить. Если же отказ произошел через промежуток времени tr< Т, то испытание продолжается, причем функция Гпересчи-тывается заново для п + 1 и / + tr.

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

  1. Интенсивность обнаружения ошибок R(t) пропорциональна текущему числу ошибок в программе, т. е. числу исходных ошибок минус обнаруженные.

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

3. Ошибки постоянно исправляются без внесения новых.

4. R(t) постоянна в промежутке между двумя смежными мо-
ментами обнаружения
г,- и tM.

Тогда R(t) ступенчатая монотонно убывающая функция вида (рис. 5.6).

Предполагается, что динамика проявления ошибок во время эксплуатации остается такой же, как при тестировании, т. е. по


t А

h

R(t)

Рис. 5.6. Вид ступенчатой монотонно убывающей функции

стулируется тождество функций R(t) и X(t) с тем отличием, что значение \(t) «замораживается» в момент завершения тестирования — прекращается исправление ошибок.

Если / — число ошибок, обнаруженных к моменту времени /, то до следующего обнаружения справедливо R(t) = К{В- /), где В — неизвестное число исходных ошибок; К неизвестный коэффициент пропорциональности. Полагая ^.= /i+I-/, (/=0, п- 1), можно утверждать, что X { имеют экспоненциальное распределение: ДА]) = exp (-К(В- i)X). Имея набор А] из журнала отладки, можно решать задачу нахождения значений К и В, отвечающих принципу максимального правдоподобия. Однако проще решение для модифицированной модели, где поскольку dN(f)/dt = R(t) (число ошибок, обнаруженных в единицу времени), получаем дифференциальное уравнение с начальными условиями:

^=KdN(t) dR(t) + Km=0 N{0)=0 т = шdt dt dt

Здесь снято допущение 4, и функция R(f) перестает быть кусочно-постоянной: R(t) = К(В- N(t)).

Его решение: R(t) = KBe~Kt Обозначая а = In (KB), b = -К, получаем запись решения в виде: R(t) = exp (я+ bt). Логарифмируя обе части этого равенства и переходя к дискретному времени получаем систему уравнений

In /?(/,) = a + btt\ i= 1, п.

Решая эту систему относительно двух неизвестных К и В методом наименьших квадратов, можно найти оценки величин К и В в соответствии с критерием максимального правдоподобия. Этот метод был применен для ряда конкретных проектов IBM и показал хорошее соответствие реальности.

5.10.3. Преимущества парного программирования

  1. Парное программирование экономически оправданно. При использовании в организации ХР штат программистов должен возрасти вдвое. Так как квалифицированные кадры являются достаточно дорогими, то, казалось бы, расходы на создание программного обеспечения должны значительно возрасти. И это действительно так — затраты возрастают на 15 %, но в то же время, как доказывают исследования, при парном программировании количество ошибок на 15 % меньше, чем при одиночном. Если учесть стоимость исправления ошибок, обнаруженных на разных стадиях жизненного цикла продукта, включая внедрение, то сэкономленные на этом деньги значительно превосходят затраты на увеличение штата сотрудников.

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

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

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

  5. Работа в паре улучшает коммуникабельность в коллективе, помогает наладить понимание и взаимоотношения, что является крайне важным фактором с точки зрения психологии [14].


5.11. Отладка программ


Локализация и исправление ошибок называется отладкой [3]. Отладка — это процесс обнаружения причин возникновения ошибки и ее последующего исправления (в отличие от тестирования, являющегося процессом обнаружения самого факта существования ошибки).

Отладка включает в себя элементы тестирования и разработки. На некоторых проектах отладка может занимать до 50 % всего времени разработки. Для многих программистов отладка — это самая трудная часть программирования. При соответствующем подходе количество ошибок, требующих отладки, должно


сократиться, и отладка должна стать самой легкой частью; при таком подходе все ошибки сводятся к небольшим недосмотрам или опечаткам.

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


Ошибки синтаксиса языка

Как правило, в абсолютном большинстве случаев ловятся на
стадии компиляции программы, или же, если вы работаете с ин-
терпретируемым языком типа
Perl или PHP, то при первом интер-
претировании программы. Но есть один существенный момент —
когда выражение допустимо, но зависит от конкретного компиля-
тора или интерпретатора. Например, в языке Си вполне допусти-
мыми по синтаксису, но не по смыслу, являются выражения:
s[i++]=i; printf ("%d %d", i++) ;. Результат этих строк

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


Ошибки во время выполнения

К ошибкам во время выполнения программы относятся ошибки в логике и алгоритмах программы. Ошибки в логике программы связаны с неправильной записью алгоритма, например, вместо && вы написали || или поставили точку с запятой там, где ее не должно быть. К этому же классу ошибок относится неправильное обращение с памятью. В языке Си неправильное обращение с памятью является наиболее распространенной ошибкой программистов, причем не только новички, но и профессионалы нередко допускают ошибки подобного типа. Для языка Си — это один из самых больных вопросов и больших недостатков. Ошибки в алгоритме связаны с неправильным проектированием программы или же с изначально неправильно заложенной бизнес-логикой и функциональными требованиями. Пользователь ждет от программы то, что изначально от нее не требовалось. Ошибки во время выполнения — наиболее часто встречаемый тип ошибок, и, как правило, устранение таких ошибок не представляется чересчур сложной задачей.


Ошибки логики взаимосвязанных СО-программ

Ошибки данного типа лежат во взаимосвязанных Сопрограммах. Рассмотрим в качестве примера тестовую систему (см. сайт http://test.itsoft.ru). При сдаче теста в цикле работают два скрипта. Первый показывает вопрос, а второй проверяет правильность ответа. Если в тесте 10 вопросов, то эти СвЬскрипты вызываются парно в цикле 10 раз. Но что будет, если пользователь нажмет кнопку «Обновить» в броузере? Скрипт, который показывает вопрос, вызовется повторно. Что будет при разрыве модемного соединения? Отладка в таких системах значительно сложнее, так как вам придется наблюдать за выполнением ряда взаимосвязанных скриптов.


Ошибки многопользовательского доступа

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


Невоспроизводимые ошибки

Невоспроизводимые ошибки представляют собой наиболее сложный тип ошибок. Например, в високосном году 29 февраля ваша система вдруг начала давать сбои, которые сами собой исчезают в невисокосном году. Но бывают ошибки, которые мистическим образом появляются и исчезают. В той же тестовой системе была непонятная ошибка, которая проявлялась 1 раз на несколько сот случаев. Непонятным образом некоторые студен-


ты после сдачи теста получали не результаты, а сбой системы. На исправление этой ошибки ушло два рабочих дня. Оказалось, что проблема в скрипте на JavaScript, который отправлял данные HTML-формы на сервер после истечения допустимого времени ответа на вопрос. Проблема в том, что если время подходило к концу и пользователь нажимал кнопку «Ответить», а в это же время уже начала работать функция JavaScript form.submit(), то отправка данных HTML-формы происходила дважды, т. е. скрипт проверки правильности ответа вызывался 2 раза. А это за собой тянуло ошибку во взаимосвязанных CGI-скриптах, и внешнее проявление сбоя системы мы наблюдали уже при подсчете результатов, а не непосредственно сразу после двойной отправки HTML-формы. Сам код JavaScript был написан верно, и с теоретической точки зрения даже если пользователь нажимает кнопку «Отправить» в последнюю секунду, HTML-форма должна была отправляться только 1 раз. Но на практике все оказалось совсем по-другому. На самом деле ничего мистического нет, или, как говорится, чудес на свете не бывает. Просто невозможно воспроизвести условия, в которых наблюдалась невоспроизводимая ошибка. Надо искать в программе случайности: одновременный доступ к одному ресурсу, генератор случайных чисел, неинициализированные переменные, некорректная работа с памятью или преобразование типов, которые могут проявлять себя не каждый раз.


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

Ошибки самого компилятора или интерпретатора очень редки, но и такие бывают.

После классификации ошибок давайте рассмотрим методы их поиска. В первую очередь, один простой и, казалось бы, очевидный совет: «надо думать, анализировать, почему программа не работает так, как было задумано, что надо в ней исправить». Никакой самый навороченный отладчик за вас ошибку не найдет. Никакая самая лучшая методика не найдет и не ускорит поиск ошибки, если вы не «включите» мозги по полной программе и не сосредоточитесь целиком и полностью на поимке ошибки. Итак, допустим, вами, пользователем или тестирующим, было зафиксировано некорректное поведение программы. Что делать? Ниже перечислены методы в порядке их приоритетности, которые используют многие программисты.