Шрифт:
Была еще одна проблема из разряда неприятных, но не смертельных. Порча памяти из-за неправильного указателя. Сама по себе порча была незначительной, но спустя несколько тысяч машинных циклов программа вылетала, потому что одна из структур данных оказывалась поврежденной. Та поврежденная структура данных использовалась постоянно, поэтому мы не могли просто добавить в код команду “Остановить программу, когда она изменится”. Я немного поразмыслил и наконец придумал двух- или трехступенчатую систему заплаток. Как только память портилась, первая заплатка активировала вторую, которая работала с другой частью кода. Это, в свою очередь, активизировало запись третьей заплатки в еще один участок. Наконец, когда эта третья заплатка замечала, что происходит что-то не то, она останавливала систему. Я придумал, как откладывать проверку до нужного момента с помощью такой динамической системы заплаток. К счастью, это сработало, и мы смогли быстро устранить неполадку.
Сейбел: Что наталкивает на подобные удачные идеи?
Козелл: Если я досконально разобрался в системе, будь то ПО для IMP или система разделения времени для PDP-1, несмотря на то, что она мультипрограммная, многослойная и основана на работе с прерываниями, все равно вся ее динамика находится у меня в голове. Я знаю, что и в каком порядке должно происходить и что не должно происходить. Это позволяет всегда представить примерную модель происходящего.
Надо сказать, что многие из возникавших тогда проблем относились к двум разным моделям компьютеров, и их анализ требовал недюжинной фантазии. Например, что-то происходит не так на первой машине, а последствия проявляются на второй. Я не могу остановить работу, первая машина уже обработала на 6000 пакетов больше, чем вторая, и вдруг вторая говорит, что один из пакетов поврежден. Что тогда делать? Всем нам троим приходилось садиться и прослеживать весь процесс в ретроспективе, чтобы найти ошибку и “вылечить” систему.
Сейбел: Был ли в систему встроен отладочный код?
Козелл: Нет.
Сейбел: Значит, с каждым сложным багом приходилось возиться отдельно?
Козелл: Насколько помню, мы не добавляли никакого отладочного кода. Сейчас я не устаю повторять: программу нужно писать так, чтобы она была тестируемой. А для того чтобы она была такой, нужно подумать об этом заранее, еще до того, как напишешь первую строку кода. Когда программа заработала, в нее уже поздно вставлять блокировки, утверждения или тестовые модули.
Но тогда мы даже не думали об этом. Мы просто старались написать эту чрезвычайно сложную систему реального времени так, чтобы она работала быстро. Это само по себе было трудной задачей. Мы не закладывали никаких проверок на непротиворечивость; к чему тратить на это время? Так что все исправления вносились потом бессистемно. Загляни в такой-то раздел памяти, просмотри код, чтобы разобраться с тем, или другим, или третьим, потом вернись и начни сначала.
Иногда эту работу удавалось несколько упорядочить. Точно помню, что написал однажды утилиту, позволявшую добавлять в систему заплатку, которая изымала один буфер из оборота и использовала его для фиксирования кода на определенных этапах. Делалось и такое, но тоже без всякой системы. Когда появлялся очередной баг, мы ломали головы, пытаясь его вычислить.
Нередко, разобравшись, что делает тот или иной баг, можно было сразу указать на часть кода, в которой он сидит. Так что достаточно было изучить ее подробно и подправить. В остальных случаях требовалось собрать больше информации. Подчас мы просто бились головой об стену, пытаясь вычленить из всей горы данных ту деталь, что давала ответ. Но в конце концов нам обычно удавалось во всем разобраться.
Нужно помнить, что мы работали на машинах с чрезвычайно примитивным, по современным меркам, управлением. В общем случае заплатка собирала некоторые данные и после этого останавливала машину. После этого приходило время использовать переднюю панель - потому что вряд ли тогда существовал отладчик, который можно было запустить с терминала, не испортив машину. Так что мы считывали нужные участки памяти с помощью передней панели, делая выкладки и пометки, чтобы понять, что, собственно, происходит.
Сейбел: Это были буквально ряды лампочек?
Козелл: Да, ряды лампочек. Одна лампочка - один бит.
Сейбел: И тумблеры для ввода адресов?
Козелл: Да. Я считаю, это лучше. На PDP-1 были тумблеры. На той машине, о которой мы говорим сейчас, помнится, были кнопки.
Сейбел: Как вам троим работалось вместе?
Козелл: У нас были разные стили программирования, тому можно вспомнить множество примеров. Уилл был великолепным интуитивным программистом. Он мог найти способ решения сложнейших задач, которые многие даже не поняли бы.
Для меня он был чем-то вроде виртуального противника в компьютерной игре, причем использующего в качестве оружия Фортран. Дело в том, что и алгоритм маршрутизации, и всю динамическую часть сиетемы управления IMP собирал Уилл. Одно из важных условий работы системы реального времени - каждой операции должна быть сопоставлена предельная продолжительность, тайм-аут. Нельзя дожидаться чего-то бесконечно долго, потому что понятия “бесконечно долго” в таких системах просто не существует.
И вот подобных тайм-аутов в программе набиралось все больше и больше. Я пытался понять все их в целом, и это было очень трудно. Однажды я решил посчитать все тайм-ауты. Например, тайм-аут подтверждения доставки сообщения должен был в восемь с небольшим раз превышать время прохождения одного пакета данных через сеть. Или тайм-аут прохождения сообщения через сеть должен был равняться максимальному диаметру сети, умноженному на время, за которое пакет передается от одного узла к другому.
И вот я думал: какими базовыми константами пользовался Уилл, собирая все это вместе? Когда два тайм-аута были одинаковыми по длине, было ли это совпадением или то был один и тот же тайм-аут? Кто знает? Сколько нужно внести изменений в код, чтобы изменить одну константу? Если в процессе работы замечаешь, что ждешь чего-то недостаточно долго и операция завершается по тайм-ауту, когда не должна, то понимаешь, что не можешь просто взять и изменить один тайм-аут в программе, потому что там все взаимосвязано.