контроля.
Тем не менее он выказывает большую проницательность. Он замечает, что
нежелание документировать проект происходит не только от лени или недостатка
времени. Оно происходит от нежелания проектировщика связывать себя
отстаиванием решений, которые, как он знает, предварительные. "Документируя
проект, проектировщик становится объектом критики со всех сторон, и должен
защищать все, что написал. Если организационная структура может представлять
угрозу, не будет документироваться ничего, кроме того, что нельзя оспорить."
Создавать организационную структуру с учетом внесения в будущем
изменений значительно труднее, чем проектировать систему с учетом будущих
изменений. Каждый получает задание, расширяющее круг его обязанностей, чтобы
сделать технически более гибким все подразделение. В больших проектах
менеджеру нужно иметь двух или трех высококлассных программистов в качестве
резерва, который можно бросить на самый опасный участок боя.
Структуру управления также нужно изменять по мере изменения системы.
Это означает, что руководитель должен уделить большое внимание тому, чтобы
его менеджеры и технический персонал были настолько взаимозаменяемы,
насколько позволяют их способности.
Барьеры являются социологическими, и с ними нужно бдительно и
настойчиво бороться. Во-первых, менеджеры сами рассматривают руководителя
как "слишком большую ценность", чтобы использовать их для реального
программирования. Во- вторых, работа менеджера обладает более высоким
престижем. Чтобы преодолеть эти сложности, в некоторых лабораториях,
например, в Bell Labs, упраздняют все наименования должностей. Каждый
профессиональный служащий является "техническим сотрудником". В других,
например в IBM, вводят двойную лестницу продвижения (рис. 11.1).
Соответствующие ступеньки теоретически равнозначны.
Рис. 11.1 Двойная служебная лестница IBM
Легко установить соответствующие ступенькам размеры жалования.
Значительно труднее дать им соответствующий престиж. Офисы должны иметь
одинаковый размер и обстановку. Секретарские и прочие службы должны быть
соответствующими. Перевод с технической лестницы в управленческую не должен
сопровождаться повышением, и о нем всегда нужно сообщать как о "переводе", а
не как о "повышении". Обратный перевод всегда должен сопровождаться
прибавкой к жалованью.
Менеджеров нужно посылать на курсы технической переподготовки, а
старший технический персонал - на курсы обучения управлению. Цели проекта,
ход работы и административные проблемы должны доводиться до всех руководящих
работников.
Если позволяет подготовка, руководящие работники должны быть технически
и морально готовы возглавить группы или насладиться разработкой программ
собственными руками. Конечно, осуществление всего этого требует много труда,
но результат того стоит!
Идея организации групп программистов наподобие операционных бригад
представляет собой коренное решение этой проблемы. Она заставляет
руководящего работника почувствовать, что он не унижает себя, когда пишет
программы, и пытается убрать социальные препятствия, мешающие ему испытать
радость творчества.
Более того, эта структура предназначена для сокращения числа
интерфейсов. Благодаря ей систему можно изменять с максимальной легкостью, и
становится относительно просто перенаправить всю бригаду на другое задание в
случае необходимости организационных изменений. Это действительно
долгосрочное решение проблемы гибкой организации.
Два шага вперед, шаг назад
Программа не перестает изменяться после своей поставки клиенту.
Изменения после поставки называются сопровождением программы, но этот
процесс в корне отличается от сопровождения аппаратной части.
Сопровождение аппаратной части компьютерной системы состоит из трех
видов деятельности: замены испорченных деталей, чистки и смазки и
осуществления технических изменений для исправления конструктивных дефектов.
(Большая часть технических изменений, но не все, устраняет дефекты
разработки или реализации, а не архитектуры, и потому незаметна
пользователю.)
Сопровождение программ не предполагает чистки, смазки или замены
испортившихся компонентов. Оно состоит главным образом из изменений,
исправляющих конструктивные дефекты. Гораздо чаще, чем для аппаратной части,
эти изменения включают в себя дополнительные функции. Обычно они видны
пользователю.
Общая стоимость сопровождения широко используемой программы обычно
составляет 40 и более процентов стоимости ее разработки. Удивительно, что на
стоимость сопровождения сильно влияет число пользователей. Чем больше
пользователей, тем больше ошибок они находят.
Бетти Кэмпбелл из Лаборатории ядерной физики МТИ отмечает интересный
цикл в жизни отдельной версии программы. Он показан на рисунке 11.2. В
начале существует тенденция повторного появления ошибок, найденных и
устраненных в предыдущих версиях. Обнаруживаются ошибки в функциях, впервые
появившихся в новой версии. Все они исправляются, и в течение нескольких
месяцев все идет хорошо. Затем количество обнаруженных ошибок снова начинает
расти. По мнению Кэмпбелл, это происходит потому, что пользователи выходят
на новый уровень сложности, начиная полностью применять новые возможности
версии. Эта интенсивная работа выявляет более скрытые ошибки в новых
функциях.5
Рис. 11.2 Частота обнаружения ошибок как функция возраста версии
программы
Фундаментальная проблема при сопровождении программ состоит в том, что
исправление одной ошибки с большой вероятностью (20-50 процентов) влечет
появление новой. Поэтому весь процесс идет по принципу "два шага вперед,
один назад".
Почему не удается устранить ошибки более аккуратно? Во-первых, даже
скрытый дефект проявляет себя как отказ в каком-то одном месте. В
действительности же он часто имеет разветвления по всей системе, обычно
неочевидные. Всякая попытка исправить его минимальными усилиями приведет к
исправлению локального и очевидного, но если только структура не является
очень ясной или документация очень хорошей, отдаленные последствия этого
исправления останутся незамеченными. Во-вторых, исправляет ошибки обычно не
автор программы, и часто это младший программист или стажер.
Вследствие внесения новых ошибок сопровождение программы требует
значительно больше системной отладки на каждый оператор, чем при любом
другом виде программирования. Теоретически, после каждого исправления нужно
прогнать весь набор контрольных примеров, по которым система проверялась
раньше, чтобы убедиться, что она каким-нибудь непонятным образом не
повредилась. На практике такое возвратное тестирование действительно должно
приближаться к этому теоретическому идеалу, и оно очень дорого стоит.
Очевидно, методы разработки программ, позволяющие исключить или, по
крайней мере, выявить побочные эффекты, могут резко снизить стоимость
сопровождения, как и методы разработки проектов меньшим числом людей и с
меньшим числом интерфейсов - а значит, и с меньшим числом ошибок.
Шаг вперед, шаг назад
Леман и Белади изучили историю последовательных выпусков большой
операционной системы.6 Они считают, что общее количество модулей растет
линейно с номером версии, но число модулей, затронутых изменениями, растет
экспоненциально в зависимости от номера версии. Все исправления имеют
тенденцию к разрушению структуры, увеличению энтропии и дезорганизации
системы. Все меньше сил тратится на исправление ошибок исходного проекта и
все больше - на ликвидацию последствий предыдущих исправлений. По прошествии
времени система становится все менее и менее организованной. Рано или поздно
исправление ошибок теряет смысл. На каждый шаг вперед приходится шаг назад.
В принципе годная для вечного использования система перестает быть основой
развития. Кроме того, меняются машины, конфигурации, требования
пользователя, так что фактически система является вечной. Необходим
совершенно новый проект, выполняемый с самого начала.
От механической статистической модели Белади и Леман приходят к общему
заключению относительно программных систем, которое подкреплено всем опытом
человечества. "Лучшая пора вещей - когда они только что появились", - сказал
Паскаль. Ч. С. Льюис выразил это более весомо:
Вот ключ к пониманию истории. Высвобождается огромная энергия,
возникают цивилизации, создаются прекрасные учреждения, но всякий раз что-то
происходит не так. Какая-то роковая ошибка возносит на вершину себялюбивых и
жестоких людей, и все скатывается назад, в нищету и руины. Действительно,
машина глохнет. Она нормально стартует и проезжает несколько метров, а затем
ломается.7
Системное программирование является процессом, уменьшающим энтропию, а
потому ему внутренне присуща метастабильность. Сопровождение программ есть
процесс, увеличивающий энтропию, и даже самое умелое его ведение лишь
отдаляет впадение системы в безнадежное устаревание.
Глава 12. Острый инструмент
Хорошего работника узнают по инструменту.
ПОСЛОВИЦА
Даже в наше время многие программные проекты, с точки зрения
использования инструментария, работают как механические мастерские. У
каждого механика есть свой набор инструментов, собиравшийся в течение всей
жизни, который он тщательно запирает и охраняет - наглядное свидетельство
личного мастерства. Точно также программист собирает маленькие редакторы,
сортировки, двоичные дампы, утилиты для работы с дисками и припрятывает их в
своих файлах.
Однако такой подход не оправдан при работе над программным проектом.
Во- первых, важной задачей является обмен информацией, а личный инструмент
ему мешает, а не содействует. Во-вторых, при переходе на новую машину или
новый рабочий язык технология меняется, поэтому срок жизни инструмента
недолог. И наконец, очевидно, значительно эффективнее совместно
разрабатывать и сопровождать программные инструменты общего назначения.
Однако недостаточно иметь инструменты общего назначения. Как
специальные задачи, так и личные предпочтения обусловливают необходимость
иметь также и специализированный инструмент. Поэтому при обсуждении состава
команды программистов я предлагал иметь в бригаде одного инструментальщика.
Этот человек владеет всеми общедоступными инструментами и может обучать их
использованию. Он может также создавать специализированные инструменты,
которые потребуются его начальнику.
Таким образом, менеджер проекта должен установить принципы и выделить
ресурсы для разработки общих инструментов. В то же время он должен понимать
необходимость в специализированных инструментах и не препятствовать
разработке собственных инструментов в подчиненных рабочих группах. Есть
опасный соблазн попытаться достичь большей эффективности, собрав вместе
отдельных разработчиков инструмента и доработав общегрупповой
инструментарий. Но это не удается.
Что это за инструменты, разработку которых менеджер должен обдумывать,
планировать и организовывать? Прежде всего, вычислительные средства. Для
этого требуются машины, и должна быть принята политика планирования времени.
Для этого требуется операционная система, и должна быть установлена политика
обслуживания. Для этого требуется язык, и должна быть заложена политика в
отношении языка. Затем идут утилиты, средства отладки, генераторы
контрольных примеров и текстовый процессор для работы с документацией.
Рассмотрим их поочередно.1
Целевые машины
Машинную поддержку полезно разделить на целевые машины и рабочие
машины. Целевая машина - это та, для которой пишется программное обеспечение
и на которой, в конце концов, его нужно будет тестировать. Рабочие машины -
это те, которые предоставляют сервисы, используемые для создания системы.
Если создается новая операционная система для старой машины, последняя может
служить одновременно и целевой, и рабочей.
Каковы типы целевых средств? Если бригада создает новый супервизор или
другое программное средство, составляющее сердцевину системы, то ей,
конечно, нужна своя машина. Для таких систем потребуются операторы и один
или два системных программиста, чтобы машина была в рабочем состоянии.
Если требуется отдельная машина, то она должна быть довольно
специфической: не требуется, чтобы она была быстрой, но требуется, по
меньшей мере, 1 Мбайт оперативной памяти, 100 Мбайт в активных дисках и
терминалы. Достаточно символьных терминалов, но со значительно большей
скоростью, чем 15 символов в секунду, характерных для пишущих машинок.
Наличие большой памяти значительно способствует продуктивности, позволяя
заняться разбиением на оверлеи и минимизацией размера после тестирования
функций.
Машина или программные средства для отладки должны также иметь средства
для автоматического подсчета и измерений любых параметров программы во время
отладки. К примеру, карты использования памяти служат мощным диагностическим
средством при выяснении странной логики поведения или неожиданно низкой
производительности.
Планирование времени. Если целевая машина новая, - например, для нее
создается первая операционная система, - то машинного времени мало, и
планирование становится большой проблемой. Потребности в рабочем времени
целевой машины имеет специфическую кривую роста. При разработке OS/360 у нас
были хорошие эмуляторы System/360 и другие машины. По прежнему опыту мы
оценили, сколько часов рабочего времени S/360 нам понадобится, и стали
получать первые машины с производства. Но месяц за месяцем они оставались
без нагрузки. Затем сразу все 16 систем оказались загруженными, и
распределение времени стало проблемой. Использование машин выглядело
примерно как на рисунке 12.1. Все одновременно начали отлаживать первые
компоненты, и затем все команды постоянно что-то отлаживали.
Рис. 12.1 Рост использования целевых машин
Мы централизовали все свои машины и библиотеки магнитных лент и
организовали для их работы профессиональную и опытную группу машинного зала.
Для максимизации бывшего в недостатке машинного времени S/360 все отладочные
прогоны мы осуществляли в пакетном режиме на подходящих свободных машинах.
Мы добились четырех запусков в день (оборачиваемость составила два с
половиной часа), а требовалась четырехчасовая оборачиваемость.
Вспомогательная машина 1401 с терминалами использовалась для планирования
прогонов, отслеживания тысяч заданий и контроля времени оборачиваемости.
Но со всей этой организованностью мы перестарались. После нескольких
месяцев низкой оборачиваемости, взаимных обвинений и прочих мук мы перешли к
выделению машинного времени крупными блоками. К примеру, вся группа из
пятнадцати человек, занимавшаяся сортировкой, получала систему на срок от
четырех до шести часов. Планирование этого времени было их внутренним делом.
Даже если система была на занята, посторонние не могли ею пользоваться.
Это оказалось более удачным способом планирования. Хотя коэффициент
использования машины, возможно, несколько упал (а часто и этого не было),
производительность поднялась. Для каждого члена команды десять запусков в
течение шести часов значительно продуктивнее, чем десять запусков,
осуществленных с перерывами в три часа, поскольку постоянная концентрация
сокращает время обдумывания. После такой гонки команде обычно требовалось
один-два дня, чтобы подогнать работу с документами, прежде чем просить о
выделении нового блока. Зачастую всего три программиста могут с пользой
поделить и распределить между собой выделенный им блок времени. Похоже, что
это лучший способ использования целевой машины при отладке новой
операционной системы.
Так было на практике, хотя это не соответствовало теории. Системная
отладка всегда была занятием для ночной смены, подобно астрономии. Двадцать
лет назад, работая над 701-й машиной, я впервые познал продуктивную свободу
от формальностей, присущую предрассветным часам, когда все начальники из
машинного зала крепко спят по домам, а операторы не расположены бороться за
соблюдение правил. Сменилось три поколения машин, полностью изменились
технологии, появились операционные системы, но этот лучший способ работы
остался прежним. Он продолжает жить, поскольку наиболее эффективен. Пришла
пора признать его продуктивность и шире применять.
Рабочие машины и службы данных
Эмуляторы. Если целевой компьютер новый, то для него необходим
логический эмулятор. Это дает аппарат для отладки задолго до того, как
целевая машина будет в наличии. Что столь же важно, даже тогда, когда
становится доступной целевая машина, имеется доступ к надежному средству для
отладки.
Надежное - не то же самое, что точное. Эмулятор неизбежно в каком-либо
отношении будет отступать от верной и точной реализации архитектуры новой
машины. Но это будет одна и та же реализация и сегодня, и завтра, чего не
скажешь о новой аппаратной части.
В наше время мы привыкли к тому, что аппаратная часть компьютера
большую часть времени работает без сбоев. Если только разработчик прикладной
программы не замечает, что система неодинаково ведет себя при разных
идентичных прогонах программы, ему правильнее всего поискать ошибки в своем
коде, а не в технике.
Этот опыт, однако, сослужил плохую службу при программировании новой
машины. Лабораторные разработки, предварительные или ранние выпуски
компьютеров не работают должным образом, не работают надежно и не остаются
неизменными день ото дня. По мере обнаружения ошибок технические изменения
производятся во всех экземплярах машины, включая используемый группой
программистов. Такая неустойчивость основания достаточно неприятна. Отказы
аппаратуры, обычно скачкообразные, еще хуже. И хуже всего неопределенность,
лишающая стимула старательно копаться в своем коде в поисках ошибки - ее
может там вовсе не быть. Поэтому надежный эмулятор на зрелой машине остается
полезным значительно дольше, чем можно было предположить.
Машины для компилятора и ассемблера. По тем же причинам требуются
компиляторы и ассемблеры, работающие на надежных машинах, но компилирующие
объектный код для целевой системы. Затем можно начать его отладку на
эмуляторе.
При программировании на языках высокого уровня значительную часть
отладки можно произвести при компиляции для вспомогательной машины и
тестировании результирующей программы, прежде чем отлаживать программу для
целевой машины. Этим достигается производительность непосредственного
исполнения, а не эмуляции, в сочетании с надежностью стабильной машины.
Библиотеки программ и учет. Очень успешным и важным применением
вспомогательной машины в программе разработки OS/360 была поддержка
библиотек программ. Система, разработанная под руководством У. Р. Кроули (W.
R. Crowley), состояла из двух соединенных вместе машин 7010 и общей дисковой
базой данных. На 7010 поддерживался также ассемблер для S/360. В этой
библиотеке хранился весь протестированный или находящийся в процессе
тестирования код, как исходный, так и ассемблированные загрузочные модули.
На практике библиотека была разбита на подбиблиотеки с различными правами
доступа.
Прежде всего, у каждой группы или программиста была область для
хранения экземпляров программ, контрольных примеров и окружения, которое
требовалось для тестирования компонентов. На этой площадке для игр не было
никаких ограничений на действия с собственными программами.
Когда компонент программиста был готов к включению в более крупную
часть, его экземпляр передавался менеджеру этой более крупной системы,
который помещал его в подбиблиотеку системной интеграции. Теперь автор не
мог его изменить без разрешения менеджера интеграции. Когда система
собиралась воедино, этот менеджер проводил все виды системного тестирования,
выявляя ошибки и получая исправления.
Через некоторое время системная версия была готова для более широкого
использования. Тогда она перемещалась в подбиблиотеку текущей версии. Этот
экземпляр был священным, и доступ к нему разрешался только для исправления
разрушительных ошибок. Его можно было использовать для интегрирования и
тестирования всех новых версий модулей. Программный каталог на машине 7010
отслеживал все версии каждого модуля, его состояние, местонахождение и
изменения.
Здесь важны два обстоятельства. Первое - это контроль, означающий, что
экземпляры программ принадлежат менеджерам, и только они могут
санкционировать их изменение. Второе - формальное разделение и перемещение с
площадки для игр к интеграции и выпуску новой версии.
По моему мнению, это было одним из лучших решений в программе OS/360.
Эта часть технологии управления была независимо разработана для нескольких
крупных программных проектов, в том числе в Bell Labs, ICL и Кембриджском
университете.2 Она применима как к программам, так и к документации. Это -
неоценимая технология.
Программные инструменты. По мере появления новых технологий отладки
старые теряют значение, но не исчезают. По-прежнему необходимы дампы памяти,
редакторы исходного текста, дампы мгновенного состояния, даже трассировки.
Аналогичным образом, требуется полный набор утилит для загрузки колод
перфокарт на диски, копирования магнитных лент, печати файлов, изменения
каталогов. Если инструментальщика проекта назначить на достаточно ранней
стадии, то все это может быть сделано сразу и находиться в готовности к
моменту надобности.
Система документации. Из всех инструментов больше всего труда может
сберечь компьютеризированная система редактирования текста, действующая на
надежной машине. Наша система, разработанная Дж. У. Франклином (J. W.
Franklin), была очень удобна. Я думаю, без нее руководства по OS/360
появились бы значительно позднее и оказались бы более запутанными. Есть
люди, которые станут утверждать, что двухметровая полка руководств по OS/360
является следствием недержания речи, и сама ее объемистость являет собой
новый тип непостижимости. И доля правды в этом есть.
Но у меня есть два возражения. Во-первых, хотя документация по OS/360 и
ошеломляет размерами, план ее изучения тщательно изложен. Если использовать
его избирательно, то чаще всего можно не обращать внимания на большую часть
всей массы. Документацию по OS/360 нужно рассматривать как библиотеку или
энциклопедию, а не материал для обязательного чтения.
Во-вторых, это гораздо лучше, чем крайняя недостаточность документации,
характерная для большинства систем программирования. Я охотно соглашусь, тем
не менее, что в некоторых местах текст можно было значительно улучшить, и
результатом лучшего описания стал бы меньший объем. Некоторые части
(например, "Концепции и средства") сейчас очень хорошо написаны.
Эмулятор производительности. Лучше его иметь. Разработайте его "снаружи
внутрь", как описано в следующей главе. Используйте одинаковое
проектирование сверху вниз для эмулятора производительности, эмулятора
логики и самого продукта. Начните работу с ним как можно раньше.
Прислушайтесь к тому, что он вам скажет.
Языки высокого уровня и интерактивное программирование
Сегодня два важнейших инструмента системного программирования - это
те, которые не использовались при разработке OS/360 почти десятилетие назад.
Они до сих пор не очень широко используются, но все указывает на их мощь и
применимость. Это: а) языки высокого уровня и б) интерактивное
программирование. Я убежден, что только инертность и лень препятствует
повсеместному принятию этих инструментов, технические трудности более не
являются извинениями.
Языки высокого уровня. Главные основания для использования языков
высокого уровня - это производительность и скорость отладки.
Производительность мы обсуждали раньше (глава 8). Имеющиеся данные, хотя и
немногочисленные, указывают на многократный рост, а не на увеличение на
несколько процентов.
Улучшение отладки происходит благодаря тому, что ошибок становится
меньше, а находить их легче. Их меньше, поскольку устраняется целый уровень
образования ошибок, уровень, на котором делаются не только синтаксические,
но и семантические ошибки, такие как неправильное использование регистров.
Их легче находить, поскольку в этом помогает диагностика компилятора и, что
еще важнее, очень легко вставлять получение отладочных моментальных снимков.
Меня эти возможности производительности и отладки ошеломляют. Мне
трудно представить себе систему программирования, которую я стал бы
создавать на языке ассемблера.
Ну, а как с классическими возражениями против этого инструмента? Их
три: я не могу сделать то, что хочу; результирующая программа слишком
велика; результирующая программа слишком медленна.
Что касается возможностей, возражение, я думаю, больше не состоятельно.
Все свидетельствует в пользу того, что можно делать то, что хочется,
потрудившись найти способ, но иногда для этого приходится изловчиться.3, 4
Что касается памяти, то новые оптимизирующие компиляторы начинают
показывать весьма удовлетворительные результаты, и их усовершенствование
продолжается.
Что касается скорости, то оптимизирующие компиляторы иногда порождают
код, который зачастую выполняется быстрее, чем написанный вручную. Более
того, проблемы скорости можно обычно решить, заменив от 1 до 5 процентов
скомпилированной программы кодом, написанным вручную, после ее полной
отладки.5
Какой язык высокого уровня следует использовать для системного
программирования? Сегодня единственный достойный кандидат - PL/I.6 У него
очень полный набор операторов; он соответствует окружению операционной
среды; имеется целый ряд компиляторов с разными особенностями -
интерактивных, быстрых, с улучшенной диагностикой, с высокой степенью
оптимизации. Лично я быстрее разрабатываю алгоритмы с помощью APL; затем я
перевожу их в PL/I для соответствия системному окружению.
Интерактивное программирование. Одним из оправданий проекта МТИ MULTICS
была его польза для создания систем программирования. MULTICS (и вслед за
тем TSS IBM) концептуально отличается от других интерактивных компьютерных
систем именно в тех отношениях, которые необходимы для системного
программирования: многоуровневая система разделения доступа и защиты данных
и программ, интенсивное управление библиотеками и средства для совместной
работы пользователей терминалов. Я убежден, что во многих приложениях
интерактивные системы никогда не заменят системы с обработкой пакетных
заданий. Но я думаю, что создатели MULTICS привели самые убедительные доводы
в ее пользу именно в применении к системному программированию.
Пока есть не много свидетельств действительной плодотворности этих
очевидно мощных инструментов. Существует широко распространенное признание
того, что отладка является трудной и медленной частью системного
программирования, и медленная оборачиваемость - проклятие отладки. Поэтому
логика интерактивного программирования кажется неумолимой.7
Рис. 12.2 Сравнительная производительность при пакетном и диалоговом
Программировании
Помимо того, есть хорошие отзывы тех, кто разработал таким способом
небольшие системы или части систем. Единственные доступные мне данные
относительно влияния на программирование больших систем исходят от Джона
Харра из Bell Labs. Они представлены на рисунке 12.2. Эти цифры охватывают
написание, ассемблирование и отладку программ. Первая программа является, в
основном, управляющей. Остальные три - языковые трансляторы, редакторы и
т.п. Данные Харра позволяют предположить, что средства интерактивной работы,
по крайней мере, удваивают производительности системного программирования.8
Эффективное использование большинства интерактивных средств требует,
чтобы работа производилась на языке высокого уровня, поскольку телетайп и
пишущую машинку нельзя использовать для получения дампа памяти. С
использованием языка высокого уровня легко редактировать исходный текст и
делать отдельные распечатки. Вместе они действительно составляют пару
отточенных инструментов.
Глава 13. Целое и части
Я духов вызывать могу из бездны.
И я могу, и каждый может,
Вопрос лишь, явятся ль на зов они?
ШЕКСПИР, КОРОЛЬ ГЕНРИХ IV
Среди современных кудесников, как и встарь, встречаются хвастуны: "Я
могу писать программы, которые управляют воздушным движением, перехватывают
баллистические ракеты, делают переводы по банковским счетам, управляют
производственными линиями". На что есть ответ: "И я могу, и каждый может, но
будет ли работать то, что ты напишешь?"
Как написать программу, которая будет работать? Как протестировать
программу? И как объединить набор протестированных программ-компонентов в
протестированную и надежную систему? Несколько раз мы уже касались
соответствующих приемов, давайте теперь рассмотрим их более систематически.
Проектирование без ошибок
Защита определений от ошибок. Самые пагубные и неуловимые системные
ошибки возникают из-за несоответствия допущений, сделанных авторами
различных компонентов. Подход к концептуальной целостности, изложенных выше
в главах 4, 5 и 6, непосредственно обращается к этим проблемам. Кратко
говоря, концептуальная целостность продукта не только упрощает его
использование, но также облегчает разработку и делает менее подверженным
ошибкам.
Такую же роль выполняет детализированная трудоемкая работа по
разработке архитектуры, подразумеваемая этим подходом. В. А. Высоцкий из
проекта Safeguard, выполнявшегося в Bell Telephone Laboratories, говорит
так: "Решающая задача - дать определение для продукта. Очень многие неудачи
связаны именно с теми аспектами, которые не были вполне специфицированы".1
Тщательное определение функций, тщательная спецификация и старательное
избегание всех украшательств функций и полетов технической мысли - все это
снижает количество системных ошибок, которые будут обнаружены.
Проверка спецификации. Задолго до написания всякого кода спецификация
должна быть передана сторонней группе тестирования для тщательного
рассмотрения полноты и ясности. Как считает Высоцкий, сами разработчики
сделать это не могут: "Они не могут признаться, что не понимают ее, они
будут счастливо прокладывать свой путь через пропущенные и темные места".
Нисходящее проектирование. В очень четкой статье 1971 года Никлаус Вирт
формализовал процедуру разработки, годами использовавшуюся лучшими
программистами.2 Более того, его замечания, сделанные в отношении разработки
программ, полностью применимы к разработке сложных программных систем.
Воплощением этих замечаний является разделение создания систем на
проектирование архитектуры, разработку и реализацию. Более того, каждая из
задач проектирования архитектуры, разработки и реализации лучше всего может
быть решена нисходящими методами.
Вкратце, метод Вирта определяет разработку как последовательность
уточняющих шагов. Набрасывается примерное описание задачи и грубый метод
решения, позволяющий получить основной результат. Затем определение
изучается более пристально, чтобы увидеть, в чем отличие полученного
результата от требуемого, и крупные этапы решения разбиваются на более
мелкие. Каждое уточнение в определении задачи становится уточнением
алгоритма решения и может сопровождаться уточнением представления данных.
В этом процессе выявляются модули решения или данных, дальнейшее
уточнение которых может быть продолжено независимо от основной работы.
Степень такой модульности определяет гибкость и изменяемость программы.
Вирт считает необходимым использование на каждом шаге нотации как можно
более высокого уровня, чтобы выделить понятия и скрыть детали, пока не
станет необходимым дальнейшее уточнение.
Правильно осуществляемое нисходящее проектирование позволяет избегать
ошибок по нескольким причинам. Во-первых, прозрачность структуры и
представления облегчает точную формулировку требований к модулям и их
функций. Во-вторых, расчленение и независимость модулей помогают избежать
системных ошибок. В- третьих, проект можно тестировать на каждом уточняющем
шаге, поэтому тестирование моно начать раньше и на каждом шаге
сосредоточиться на подходящем уровне детализации.
Процесс пошагового уточнения не означает, что в случае столкновения с
какой- нибудь неожиданно затруднительной деталью не приходится возвращаться
назад, отбрасывать самый верхний уровень и начинать все сначала. На практике
это часто случается. Но становится значительно легче точно увидеть, когда и
почему нужно отбросить весь проект и начать сначала. Многие слабые системы
появляются в результате попыток сохранить скверный первоначальный проект
путем разного рода косметических заплаток. Нисходящее проектирование
уменьшает такой соблазн. Я убежден, что нисходящее проектирование является
важнейшей новой формализацией программирования за десятилетие.
Структурное программирование. Другой важный круг идей для разработки,
сокращающих число ошибок в программе, исходит то Дейкстры (Dijkstra)3 и
построен на теоретической структуре Бема (Boehm) и Джакопини (Jacopini).4
В своей основе подход заключается в разработке программ, управляющие
структуры которых состоят только из циклов, определяемых такими операторами,
как DO WHILE и группами условно выполняемых операторов, ограниченных
скобками с использованием операторов условия IF...THEN...ELSE. Бем и
Джакопини показывают теоретическую достаточность таких структур. Дейкстра
доказывает, что альтернативное неограниченное применение ветвление с помощью
GO TO образует структуры, располагающие к появлению логических ошибок.
В основе, несомненно, лежат здравые мысли. При обсуждении сделано много
критических замечаний - в частности, большое удобство представляют
дополнительные управляющие структуры, такие как n-вариантный переход (так
называемый оператор CASE) для различения среди нескольких случаев и
аварийный выход (GO TO ABNORMAL END). Кроме того, некоторые догматически
избегают всех GO TO , что представляется чрезмерным.
Важной и существенной для создания программ, не содержащих ошибок,
является необходимость рассматривать управляющие структуры системы как
управляющие структуры, а не как отдельные операторы перехода. Такой образ
мысли является большим шагом вперед.
Отладка компонентов
За последние двадцать лет процедуры отладки программ прошли большой
круг и в некоторых отношениях вернулись к начальной точке. Цикл прошел
четыре этапа и любопытно проследить их, отметив мотивацию перехода.
Отладка в активном режиме. У первых машин было сравнительно слабое
оборудование ввода-вывода, обусловливавшее большие задержки. Обычно машина
использовала для чтения и записи бумажные и магнитные ленты, а для
подготовки лент и печати использовались автономные средства. Из-за этого
ввод-вывод на ленту был невыносимо неудобен для отладки, и для нее
использовалась консоль. Поэтому отладка организовывалась таким образом,
чтобы обеспечить за сеанс работы с машиной возможно большее число проверок.
Программист тщательно разрабатывал свои процедуры отладки, планируя
места остановки, адреса памяти для просмотра, их возможное содержимое и
дальнейшие действия в зависимости от содержимого. Это дотошное
программирование самого себя в качестве отладчика вполне могло занять
половину времени написания отлаживаемой программы.
Главным грехом было смело нажать кнопку START, не разбив предварительно
программу на отлаживаемые секции с запланированными остановками.
Дампы памяти. Отладка в активном режиме была очень эффективной. За
двухчасовую отладку можно было запустить программу раз десять. Но компьютеры
были малочисленны и очень дороги, и мысль о такой напрасной трате машинного
времени ужасала.
Поэтому, когда появились скоростные принтеры, подключаемые в активном
режиме, технология изменилась. Программа запускалась и работала до
возникновения ошибки, после чего распечатывался дамп памяти. Тогда начинался
кропотливый труд за столом по изучению содержимого каждого адреса. Времени
уходило примерно столько же, сколько и при отладке на машине, но это было
уже после контрольного прогона, и работа состояла в расшифровке данных, а не
в планировании, как прежде. Для каждого отдельного пользователя отладка
занимала значительно больший срок, поскольку тестовые запуски зависели от
оборачиваемости пакетной обработки. Однако процедура в целом была
предназначена для сокращения времени использования компьютера и обслуживания
возможно большего числа программистов.
Снимки моментального состояния. Машины, для которых были разработаны
дампы памяти, имели память размером 2000-4000 слов, или 8-16 Кбайт. Однако
размер памяти рос огромными темпами, и делать дамп памяти стало нереальным.
Поэтому разработали методы выборочного дампа, выборочной трассировки и
вставки в программы команд для моментальных снимков. Вершиной развития этого
направления стал TESTRAN в OS/360, позволявший вставлять в программу
моментальные снимки без повторной сборки и компиляции.
Интерактивная отладка. В 1959 году Кодд (Codd) с коллегами5 и Стрейчи
(Strachey)6 сообщили о работе, целью которой была отладка в режиме
разделения времени, позволяющая одновременно достичь мгновенной
оборачиваемости отладки в активном режиме и эффективно использовать машинное
время, как при пакетной обработке заданий. Компьютер должен был иметь в
памяти несколько программ, готовых к запуску. Терминал, управляемый только
программой, должен был быть связан с каждой из отлаживаемых программ.
Отладка должна была проходить под управлением программы-супервизора. Когда
программист за терминалом останавливал свою программу, чтобы изучить ее
выполнение или внести изменения, супервизор запускал другую программу,
занимая таким образом машину.
Мультипрограммная система Кодда была разработана, но акцент был сделан
на увеличение производительности благодаря эффективному использованию ввода-
вывода, и интерактивная отладка не была осуществлена. Идеи Стрейчи были
улучшены и в 1963 году воплощены Корбато с коллегами в МТИ в
экспериментальной системе 7090. Это разработке привела к MULTICS, TSS и
другим сегодняшним системам разделения времени.
Главными ощущаемыми пользователем различиями между отладкой в активном
режиме, как она осуществлялась ранее, и сегодняшней интерактивной отладкой
являются возможности, полученные в результате присутствия программы-
супервизора и связанных с ней интерпретаторов языков программирования. Можно
программировать и производить отладку на языках высокого уровня. Эффективные
средства редактирования позволяют легко делать изменения и моментальные
снимки.
Возврат к мгновенной оборачиваемости отладки в активном режиме пока не
привел к возвращению предварительного планирования отладочных сеансов. В
сущности, такое предварительное планирование не столь необходимо, как
раньше