• 17 Переносимость: переносимость программ и соблюдение стандартов
  • 17.1. Эволюция С
  • 17.1.1. Ранняя история С
  • 17.1.2. Стандарты С
  • 17.2. Стандарты Unix
  • 17.2.1. Стандарты и Unix-войны
  • 17.2.2. Влияние новых Unix-систем
  • 17.2.3. Стандарты Unix в мире открытого исходного кода
  • 17.3. IETF и процесс RFC-стандартизации
  • 17.4. Спецификации — ДНК, код — РНК
  • 17.5. Программирование, обеспечивающее переносимость
  • 17.5.1. Переносимость и выбор языка
  • 17.5.1.1. Переносимость С
  • 17.5.1.2. Переносимость С++
  • 17.5.1.3. Переносимость shell
  • 17.5.1.4. Переносимость Perl
  • 17.5.1.5. Переносимость Python
  • 17.5.1.6. Переносимость Tcl
  • 17.5.1.7. Переносимость Java
  • 17.5.1.8. Переносимость Emacs Lisp
  • 17.5.2. Обход системных зависимостей
  • 17.5.3. Инструменты, обеспечивающие переносимость
  • 17.6. Интернационализация
  • 17.7. Переносимость, открытые стандарты и открытый исходный код
  • 18 Документация: объяснение кода в Web-сообществе
  • 18.1. Концепции документации
  • 18.2. Стиль Unix
  • 18.2.1. Склонность к большим документам
  • 18.2.2. Культурный стиль
  • 18.3. Многообразие форматов документации в Unix
  • 18.3.1. troff и инструментарий Documenter's Workbench
  • 18.3.2. TEX
  • 18.3.3. Texinfo
  • 18.3.4. POD
  • 18.3.5. HTML
  • 18.3.6. DocBook
  • 18.4. Современный хаос и возможный выход из положения
  • 18.5. DocBook
  • 18.5.1. Определения типов документов
  • 18.5.2. Другие DTD-определения
  • 18.5.3. Инструментальная связка DocBook
  • 18.5.4. Средства преобразования
  • 18.5.5. Инструменты редактирования
  • 18.5.6. Связанные стандарты и практические приемы
  • 18.5.7. SGML
  • 18.5.8. Справочные ресурсы по XML-DocBook
  • 18.6. Лучшие практические приемы написания Unix-документации
  • 19 Открытый исходный код: программирование в новом Unix-сообществе
  • 19.1. Unix и открытый исходный код
  • 19.2. Лучшие практические приемы при взаимодействии с разработчиками открытого исходного кода
  • 19.2.1. Хорошая практика обмена исправлениями
  • 19.2.1.1. Отправляйте заплаты, а не целые архивы или файлы
  • 19.2.1.2. Отправляйте исправления к текущей версии кода
  • 19.2.1.3. Не следует включать заплаты для генерируемых файлов
  • 19.2.1.4. Не отправляйте заплат, которые только убирают $-идентификаторы систем RCS или SCCS
  • 19.2.1.5. Используйте вместо формата по умолчанию (
    -e
    ) форматы
    или
    -u
  • 19.2.1.6. Сопровождайте заплаты документацией
  • 19.2.1.7. Сопровождайте заплату пояснениями
  • 19.2.1.8. Включайте в код полезные комментарии
  • 19.2.1.9. Не огорчайтесь, если заплата отклонена
  • 19.2.2. Хорошая практика наименования проектов и архивов
  • 19.2.2.1. Используйте GNU-стиль названий с именной частью и номерами (основной.второстепенный.заплата)
  • 19.2.2.2. По возможности необходимо придерживаться локальных соглашений
  • 19.2.2.3. Упорно ищите уникальный префикс имени, который легко вводить
  • 19.2.3. Хорошая практика разработки
  • 19.2.3.1. Не полагайтесь на частный код
  • 19.2.3.2. Используйте автоинструменты GNU
  • 19.2.3.3. Тестируйте код перед выпуском версии
  • 19.2.3.4. Выполняйте контроль ошибок в коде перед выпуском версии
  • 19.2.3.5. Проверяйте орфографию в документации и README-файлах перед выпуском версии
  • 19.2.3.6. Рекомендованные практические приемы переносимости кода C/C++
  • 19.2.4. Хорошая практика создания дистрибутивов
  • 19.2.4.1. Убедитесь, что архивы всегда распаковываются в один новый каталог
  • 19.2.4.2. Включайте в дистрибутив README-файл
  • 19.2.4.3. Придерживайтесь стандартной практики именования файлов
  • 19.2.4.4. Проектирование с учетом обновлений
  • 19.2.4.5. В Linux создавайте RPM-пакеты
  • 19.2.4.6. Предоставляйте контрольные суммы пакетов
  • 19.2.5. Практические приемы хорошей коммуникации
  • 19.2.5.1. Публикация на сайте Freshmeat
  • 19.2.5.2. Публикация в соответствующих группах новостей
  • 19.2.5.3. Создайте Web-сайт
  • 19.2.5.4. Поддерживайте списки рассылки проекта
  • 19.2.5.5. Публикуйте проект в главных архивах
  • 19.3. Логика лицензирования: как выбрать лицензию
  • 19.4. Почему следует использовать стандартную лицензию
  • 19.5. Многообразие лицензий на открытый исходный код
  • 19.5.1. Лицензия MIT или Консорциума X
  • 19.5.2. Классическая BSD-лицензия
  • 19.5.3. Артистическая лицензия
  • 19.5.4. General Public License
  • 19.5.5. Mozilla Public License
  • 20 Будущее: опасности и перспективы
  • 20.1. Сущность и случайность в традиции Unix
  • 20.2. Plan 9: каким представлялось будущее Unix
  • 20.3. Проблемы в конструкции Unix
  • 20.3.1. Unix-файл представляет собой только большой блок байтов
  • 20.3.2. Слабая поддержка GUI-интерфейсов в Unix
  • 20.3.3. Удаление файлов в Unix необратимо
  • 20.3.4. Unix предполагает статичную файловую систему
  • 20.3.5. Конструкция системы управления задачами была плохо реализована
  • 20.3.6. В Unix API не используются исключительные ситуации
  • 20.3.7. Вызовы ioctl(2) и fcntl(2) являются препятствиями
  • 20.3.8. Модель безопасности Unix, возможно, слишком примитивна
  • 20.3.9. Unix имеет слишком много различных видов имен
  • 20.3.10. Файловые системы могут считаться вредными
  • 20.3.11. На пути к глобальному адресному пространству Internet
  • 20.4. Проблемы в окружении Unix
  • 20.5. Проблемы в культуре Unix
  • 20.6. Причины верить
  • Часть IV

    Сообщество

    17

    Переносимость: переносимость программ и соблюдение стандартов

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

    (Переносимость С-программ и операционной системы Unix (Portability of С Programs and the UNIX System, 1978)) (–Стив Джонсон, Деннис Ритчи)

    Unix была первой действующей операционной системой, переносимой между различными семействами процессоров (Version 6 Unix, 1976 - 1977). В настоящее время Unix регулярно переносится на все новые машины, мощность которых достаточна для поддержки блока управления памятью. Unix-приложения просто переносятся между Unix-системами, работающими на различном аппаратном обеспечении; фактически, неудачные попытки переноса не известны.

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

    Unix-программисты знают по опыту, что очень легко ошибиться, предрекая короткую жизнь программному проекту[136]. Поэтому они склонны избегать создания программного обеспечения, зависимого от специфических и недолговечных технологий, и в большой степени полагаются на открытые стандарты. Привычка писать программы с учетом переносимости уже стала одной из традиций Unix, так что теперь она относится даже к мелким проектам, которые задумывались как однократно используемый код. Она оказала влияние на всю конструкцию инструментария Unix-разработчика, а также на языки программирования, такие как Perl, Python и Tcl, которые создавались в Unix-среде.

    Непосредственный выигрыш от переносимости заключается в том, что для Unix-программ является нормой пережить свою исходную аппаратную платформу, поэтому не требуется каждые несколько лет заново изобретать инструменты и приложения. Сегодня приложения, первоначально написанные для Version 7 Unix (1979), регулярно используются не только на Unix-системах, "генетически" происходящих от V7, но и на таких вариантах системы, как Linux, в которой API-интерфейс операционной системы был написан на основании Unix-спецификации и не содержит кода, заимствованного у Bell Labs.

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

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

    17.1. Эволюция С

    Главным фактом практики Unix-программирования всегда была стабильность языка С и небольшого количества служебных интерфейсов, которые всегда ему сопутствовали (особенно стандартная I/O-библиотека и подобные ей). Тот факт, что язык, созданный в 1973 году, в течение тридцати лет интенсивного использования потребовал небольших изменений, является действительно примечательным, в этом отношении язык С не имеет аналогов в компьютерной науке.

    В главе 4 приводились аргументы в пользу того, что С достиг успеха, благодаря тому, что он выполняет роль тонкого связующего уровня над аппаратным обеспечением компьютера, приближающимся к "стандартной архитектуре" [4]. Однако для того чтобы понять другие факторы, необходимо кратко рассмотреть историю данного языка.

    17.1.1. Ранняя история С

    Язык С родился в 1971 году как язык системного программирования для PDP-11-варианта Unix и основывался на раннем интерпретаторе языка В, разработанного Кеном Томпсоном. Язык В, в свою очередь, был смоделирован с базового языка общего программирования (Basic Common Programming Language — BCPL), разработанного в Кембриджском университете в 1966–1967 годах[137].

    Первоначальный C-компилятор Денниса Ритчи (который часто называли "DMR" по инициалам его создателя) обслуживал сообщество, быстро разрастающееся вокруг операционной системы Unix версий 5, 6 и 7. От шестой версии С языка произошла версия С компании Whitesmiths, повторная реализация, которая стала первым коммерческим C-компилятором и ядром IDRIS, первого клона Unix. Однако самые современные реализации С смоделированы на основе "переносимого C-компилятора" (Portable С Compiler — PCC) Стивена Джонсона (Steven С. Johnson), который дебютировал в седьмой версии и полностью заменил компилятор DMR как в ветви System V, так и в BSD-версиях четвертого поколения.

    В 1976 году в шестой версии С были представлены объявления

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

    Оригинальным описанием языка С была книга Брайана Кернигана и Денниса Ритчи "The С Programming Language", которую также называли "Белой книгой" [42]. Она была опубликована в 1978 году, в том же году появился C-компилятор Whitemiths.

    Белая книга описывала усовершенствованную шестую версию языка С с одним значительным исключением, которое касалось обработки общедоступных переменных. Первоначально Ритчи намеревался смоделировать правила С с COMMON- объявлений языка FORTRAN, основываясь на теоретическом соображении, что любая машина, способная поддерживать FORTRAN, будет готова к использованию С. В модели с общим блоком общедоступная переменная может быть объявлена несколько раз; идентичные объявления объединяются компоновщиком. Однако два ранних варианта С (для мэйнфреймов Honeywell и IBM 360) работали на машинах с очень ограниченной общей памятью или примитивным компоновщиком, или имели оба недостатка. Таким образом, компилятор Version 6 С был перенесен в боле строгую модель определения (которая требовала максимум одно определение любой заданной общедоступной переменной и ключевого слова

    extern
    , задающего на нее ссылки), описанную в [42].

    Это решение было отменено в C-компиляторе, который поставлялся с Version 7, после выяснения того, что большое количество существующего исходного кода зависело от более свободных правил. Под давлением обратной совместимости закончилась неудачей еще одна попытка перестроиться (в версии System V Release 1 1983 года), перед тем как в 1988 году в проекте стандарта ANSI (ANSI Draft Standard) окончательно установились правила определения. Общий блок внешних переменных до сих пор является общепризнанным отклонением от стандарта.

    В седьмой версии С была введена конструкция

    enum
    , а значения
    struct
    и
    union
    интерпретировались как объекты первого класса, которые можно назначать, передавать в качестве аргументов и возвращать из функций (вместо передачи по адресу).

    Другим главным изменением в V7 было то, что Unix-объявления структур данных теперь документировались в файлах заголовков и подключались. В предыдущих версиях Unix структуры данных (например, для каталогов) фактически печатались в руководстве, из которого программисты копировали их в свой код. Излишне говорить, что это было значительной проблемой для переносимости.

    (Стив Джонсон.)

    Версия System III С компилятора PCC (которая также поставлялась с BSD 4.1с) изменила обработку объявлений

    struct
    так, чтобы члены с одинаковыми именами в различных структурах не конфликтовали. Также были внесены объявления
    void
    и
    unsigned char
    . Область действия extern-объявлений локальных для функций была ограничена данной функцией и более не включала всего следующего за ней кода.

    В проекте стандарта ANSI С (ANSI С Draft Proposed Standard) были добавлены определения

    const
    (для памяти только для чтения) и
    volatile
    (для таких ячеек, как отображаемые на память регистры ввода/вывода, которые можно модифицировать асинхронно из потока управления программы). В целях применения к любому типу был обобщен модификатор типа
    unsigned
    , а также добавлен идентификатор
    signed
    . Был добавлен синтаксис для
    auto
    -массива, инициализаторов структур, а также типов
    union
    . Наиболее важно то, что были добавлены прототипы функций.

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

    Более подробно история раннего С описана создателем языка в статье [70].

    17.1.2. Стандарты С

    Разработка стандартов С была консервативным процессом, в котором серьезное внимание уделялось "сохранению духа" оригинального С, а акцент был сделан скорее на утверждении экспериментов в существующих компиляторах, чем на создании новых функций. Документ-хартия C9X (C9X charter)[138] является превосходным выражением данной миссии.

    Работа над первым официальным стандартом С началась в 1983 году при содействии комитета X3J11 ANSI. Главные функциональные дополнения к языку были утверждены к концу 1986 года, и с этого момента программисты стали различать "K&R С" и "ANSI С".

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

    (Генри Спенсер.)

    Пустые указатели были созданы как часть работы по стандартизации и стали успешными. Но точку зрения Генри принимают до сих пор.

    (Стив Джонсон.)

    Несмотря на то, что основа ANSI С была согласована достаточно рано, споры о содержимом стандартных библиотек продолжались в течение нескольких лет. Формальный стандарт не был опубликован до конца 1989 года, сразу после этого в большинстве компиляторов были реализованы рекомендации 1985 года. Данный стандарт первоначально назывался ANSI Х3.159, но был переименован в ISO/IEC 9899:1990, когда Международная организация по стандартизации (International Standards Organization — ISO) приняла на себя спонсорские обязательства. Описываемый в стандарте вариант языка обычно называется C89 или C90.

    Первая книга по С и практической реализации переносимости, "Portable С and Unix Systems Programming" [44], была опубликована в 1987 году. (Я написал ее под корпоративным псевдонимом, навязанным мне моими тогдашними работодателями.) Второе издание книги Кернигана и Ритчи [42] вышло в 1988 году.

    Очень незначительная модификация C89, названная Amendment 1 (поправка 1), AM1, или C93, появилась в 1993 году. В ней было добавлено больше возможностей по поддержке широких символов и Unicode. Данный стандарт получил название ISO/IEC 9899-1:1994.

    Пересмотр стандарта C89 начался в 1993 году. В 1999 году организация ISO приняла стандарт ISO/IEC 9899 (обычно называемый C99). Он включал в себя Amendment 1 и большое количество мелких функций. Возможно, наиболее значительной из них для большинства программистов является возможность объявлять переменные в любом месте блока, а не только в начале (аналогичная языку С++). Кроме того, в стандарте были добавлены макросы с переменным числом аргументов.

    Рабочая группа C9X имеет Web-страницу <http://anubis.dkuug.dk/JTC1/SC22/WG14/www/projеcts>, но к середине 2003 года планов на третий проект стандарта не было. Члены рабочей группы разрабатывают дополнения к С для встроенных систем.

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

    17.2. Стандарты Unix

    Тот факт, что в 1973 году Unix была переписана на С, беспрецедентно упростил переносимость и модификацию операционной системы. В результате исходная Unix вскоре разделилась на семейство операционных систем. Unix-стандарты первоначально разрабатывались в целях согласования API-интерфейсов различных ветвей фамильного дерева.

    Unix-стандарты, развившиеся после 1985 года, были весьма успешными в этом отношении — настолько успешными, что легли в основу высоко ценимой документации по API современных реализаций Unix. Фактически реальные Unix-системы так близко придерживаются опубликованных стандартов, что разработчики могут (а часто так и делают) узнать больше, изучая такие документы, как спецификация POSIX, чем официальные справочные руководства для используемого варианта Unix.

    Действительно, в наиболее новых Unix-системах с открытым исходным кодом (таких как Linux) широко распространена практика проектирования функций операционной системы с использованием опубликованных стандартов как спецификации. Этот момент рассматривается в ходе изучения стандартов RFC далее в настоящей главе.

    17.2.1. Стандарты и Unix-войны

    Первоначальным мотивом для разработки Unix-стандартов было расхождение линий разработки AT&T и Университета Беркли, которое рассматривалось в главе 7.

    Unix-системы 4.x BSD происходили от Version 7, вышедшей в 1979 году. После выхода 4.1 BSD в 1980 году BSD-линия быстро приобрела репутацию революционной Unix-системы. В число важных дополнений входили визуальный редактор vi, средства контроля заданий для управления с одной консоли многочисленными высокоприоритетными и фоновыми задачами, а также усовершенствование сигналов (см. главу 7). Несравненно более важным дополнением была поддержка TCP/IP-сетей, но хотя Университет Беркли получил контракт на их реализацию в 1980 году, протокол TCP/IP в течение трех лет не включался в состав внешних версий.

    Однако другая версия, System III, появившаяся в 1981 году, стала основой дальнейшей разработки в корпорации AT&T. В System III интерфейс терминалов версии 7 был переработан в более четкую и изящную форму, которая была абсолютно несовместимой с усовершенствованиями Беркли. В ней сохранялась (необратимая) семантика сигналов (см. главу 7). В состав январской версии System V Release 1 в 1983 году вошли некоторые BSD-утилиты (такие как vi(1)).

    Первая попытка заполнить разрыв была предпринята в феврале 1983 года влиятельной группой Unix-пользователей, UniForum. Вышедший в 1983 году проект стандарта Uniforum (Uniforum 1983 Draft Standard — UDS 83) описывал "основную Unix систему", состоящую из подмножества ядра System III и библиотек плюс примитив блокировки файлов. AT&T объявила о поддержке UDS 83, однако данный стандарт представлял собой неадекватный набор разрабатываемых практических приемов 4.1BSD. Проблема обострилась с выходом в июле 1983 года 4.2BSD, в которой было добавлено множество новых функций (включая TCP/IP-сети) и введена некоторая неочевидная несовместимость с исходной версией 7.

    Лишение прав компаний Bell в 1984 году и начало Unix-войн (см. главу 2) значительно усложнило проблемы. Sun Microsystems лидировала в индустрии рабочих станций в BSD-направлении; AT&T пыталась внедриться в компьютерный бизнес и использовать контроль над Unix как стратегическое оружие, даже продолжая лицензировать операционную систему таким конкурентам, как Sun. Все поставщики принимали бизнес-решения дифференцировать свои версии Unix для получения конкурентного преимущества.

    В ходе Unix-войн техническая стандартизация стала точкой зрения, которую отстаивали сотрудничающие технические специалисты, а большинство менеджеров по продуктам принимали ее неохотно или активно сопротивлялись. Одним крупным и важным исключением была корпорация AT&T, которая, анонсируя в январе 1984 года System V Release 2 (SVr2), объявила о намерении сотрудничать с группами пользователей по формированию стандартов. Второй пересмотр проекта стандарта UniForum в 1984 году проложил путь и одновременно повлиял на API SVr2. Позднее Unix-стандарты также стремились следовать System V, кроме тех областей, где BSD-средства имели очевидное функциональное превосходство (поэтому, например, современные стандарты Unix описывают средства управления терминалами System V, вместо BSD-интерфейса к тем же средствам).

    В 1985 году AT&T опубликовала документ System V Interface Definition (SVID), в котором содержалось более формальное описание SVr2 API, включающего стандарт UDS 84. Более поздние модификации SVID2 и SVID3 наметили курс на создание интерфейсов System V версий 3 и 4. Документ SVID стал основой POSIX-стандартов, которые в конечном итоге склонили большую часть спора Беркли/АТ&Т по поводу системных и библиотечных вызовов С в сторону подхода AT&T.

    Однако это не стало очевидным в течение еще нескольких лет. Тем временем Unix-войны были в разгаре. Например, в 1985 году вышли два конкурирующих API-стандарта для файловой системы с совместным доступом по сети: Network File System (NFS) корпорации Sun и АТ&Т-система Remote File System (RFS). NFS-система выиграла, поскольку Sun была готова разделить с другими не только спецификации, но и открытый исходный код.

    Это был настоящий успех, потому что по своему логическому построению система RFS была все-таки лучшей моделью. Она поддерживала лучшую семантику блокировки файлов и лучшую идентификацию пользователей в различных системах и, в отличие от NFS, как правило, была направлена на точное получение более мелких деталей семантики файловой системы Unix. Однако данный урок был проигнорирован, даже когда в 1987 году его повторила система X Window с открытым исходным кодом, победив частную систему Sun Networked Window System (NeWS).

    После 1985 года главные вопросы по Unix-стандартизации решались Институтом инженеров электротехники и электроники (Institute of Electrical and Electronic Engineers — IEEE). Комитет 1003 IEEE разработал серию стандартов, которые широко известны как POSIX[139]. Данные стандарты выходили за рамки простых системных вызовов и библиотечных средств С. Они определяли подробную семантику оболочки и минимальный набор команд, а также детальные привязки для различных языков программирования, отличных от С. Первая версия появилась в 1990 году, а вторая редакция была опубликована в 1996 году. Международная организация по стандартизации (International Standards Organization — ISO) приняла данные стандарты под названием ISO/IEC 9945.

    Ниже перечислены некоторые из ключевых POSIX-стандартов.

    1003.1 (опубликован в 1990 году)

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

    1003.2 (опубликован в 1992 году)

    Стандартная оболочка и утилиты. Семантика оболочки строго повторяет семантику Bourne shell в System V.

    1003.4 (опубликован в 1993 году)

    Unix-система реального времени. Бинарные семафоры, блокировка памяти процесса, файлы с распределением памяти, приоритетное расписание, сигналы реального времени, циклы и таймеры, передача IPC-сообщений, синхронизированный I/O, асинхронный I/O, файлы реального времени.

    Во втором издании 1996 года стандарт 1003.4 был разделен на 1003.1b (система реального времени) и 1003.1с (параллельные процессы).

    Несмотря на то, что некоторые ключевые области, такие как семантика обработки сигналов и упущение BSD-сокетов, были недоопределены, первоначальные POSIX-стандарты стали основой всей последующей работы по стандартизации Unix. Они до сих пор цитируются как авторитетные, хотя и косвенно, посредством таких справочников, как "POSIX Programmer's Guide" [47]. Де-факто стандарт Unix API до сих пор остается "POSIX плюс сокеты", более поздние стандарты, главным образом, добавляют функции и более точно определяют согласование в необычных крайних случаях.

    Следующим игроком на этом поле была группа X/Open (позднее переименованная в Open Group), консорциум поставщиков Unix, сформированный в 1984 году. Стандарты данной организации X/Open Portability Guides (XPGs) первоначально развивались параллельно с проектами POSIX, а затем после 1990 года стандарты XPGs были включены в POSIX и расширили его. В отличие от POSIX, который представлял собой попытку сбора надежного подмножества из всех Unix-систем, стандарты XPGs больше склонялись к общей практике в наиболее развитом участке исследований; даже XPG1 в 1985 году, охватывающий системы SVr2 и 4.2BSD, включал в себя сокеты.

    В 1987 году в стандарт XPG2 был добавлен API-интерфейс поддержки терминалов, которым, по существу, была библиотека curses(3) системы System V. XPG3 в 1990 году влился в X11 API. XPG4 в 1992 году полностью утвердил полное соответствие стандарту 1989 года ANSI С. В стандартах XPG2, 3 и 4 интенсивно рассматривалась поддержка интернационализации и описывался сложный API для обработки наборов кодов и каталогов сообщений.

    В материалах по стандартам Unix могут встретиться ссылки на стандарты "Spec 1170" (1993 года), "Unix 95" (1995 года) и "Unix 98" (1998 года). Это были сертификационные знаки стандартов X/Open, и в настоящее время они представляют только исторический интерес. Однако работа над стандартом XPG4 превратилась в спецификацию Spec 1170, которая стала первой версией Единой спецификации Unix (Single Unix Specification, SUS).

    В 1993 году 75 поставщиков систем и программного обеспечения, включая все главные Unix-компании, поставили окончательную точку в Unix-войнах, когда объявили о финансовой поддержке X/Open в разработке общего определения Unix. Как часть данного соглашения X/Open приобрела права на торговую марку Unix. Объединенный стандарт стал называться Single Unix Standard (Единый стандарт Unix) версии 1. Версия 2 вышла в 1997 году. В 1999 году деятельность по стандарту POSIX перешла к X/Open.

    В 2001 году группа X/Open (сейчас The Open Group) выпустил Единый стандарт Unix версии 3 (Single Unix Standard version 3) <http://www.Unix.org/version3/>. Все "потоки" стандартизации Unix API были, наконец, объединены в одно целое. Различные вариации Unix воссоединились на основе общего API. И это было встречено с большим энтузиазмом, по крайней мере, среди давних поклонников Unix, которые помнили о бурях 80-х годов.

    17.2.2. Влияние новых Unix-систем

    К сожалению, была одна неудобная деталь — поставщики Unix старой школы, финансирующие данный проект, находились под сильным влиянием со стороны Unix-систем новой школы с открытым исходным кодом, а в некоторых случаях они даже отказывались (в пользу Linux) от коммерческих Unix-систем, ради которых они потратили так много усилий для достижения надежного соответствия.

    Тестирование соответствия, необходимое для проверки соответствия Единой спецификации Unix, — дорогая проблема. Оно должно осуществляться для каждого дистрибутива, но не учитывает многообразия дистрибьюторов операционных систем с открытым исходным кодом. В любом случае, Linux изменяется столь быстро, что любая версия дистрибутива, вероятно, устарела бы к моменту окончания сертификации[140].

    Стандарты, подобные Single Unix Specification, не утратили полностью своей значимости. Они все еще являются ценными руководящими принципами для разработчиков Unix. Однако далее стоит рассмотреть то, как "The Open Group" и другие организации, связанные с процессом стандартизации Unix старой школы, будут приспосабливаться к быстрому темпу выхода версий с открытым исходным кодом (а также к малобюджетному или безбюджетному функционированию групп разработчиков программного обеспечения с открытым исходным кодом).

    17.2.3. Стандарты Unix в мире открытого исходного кода

    В середине 1990-х годов сообщество открытого исходного кода начало собственную работу по стандартизации. Эти усилия основывались на совместимости на уровне кода, закрепленной стандартом POSIX и его потомками. В частности Linux, была написана с нуля способом, который зависел от доступности таких стандартов Unix API, как POSIX[141].

    В 1998 году компания Oracle перенесла на Linux свой лидирующий на рынке баз данных продукт, этот шаг справедливо рассматривается как главный прорыв в движении принятия Linux. Ответственный инженер проекта, в подтверждение того, что API-стандарты выполнили свою задачу, на вопрос репортера о том, какие технические трудности пришлось преодолеть Oracle ответил: "Мы вводили make".

    Таким образом, проблемой для Unix-систем новой школы была не API-совместимость на уровне исходного кода. Все принимают как должное возможность переносить исходный код между различными Linux, BSD и коммерческими дистрибутивами Unix без необходимости тратить серьезные усилия на обеспечение переносимости кода. Новой проблемой была не совместимость исходного кода, а бинарная совместимость. Почва под Unix неожиданно зашаталась вследствие массового спроса на аппаратное обеспечение PC.

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

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

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

    Первым проектом по стандартизации, который развился внутри нового сообщества Linux и BSD (начиная с 1993 года) был Стандарт иерархии файловой системы (Filesystem Hierarchy Standard — FHS). Он был включен в состав Базы стандартов Linux (Linux Standards Base — LSB), которая также стандартизировала ожидаемый набор служебных библиотек и вспомогательных приложений. Оба стандарта стали результатом деятельности Группы свободных стандартов (Free Standards Group) <http://www.freestandards.org/>, которая к 2001 году стала играть роль аналогичную позиции консорциума X/Open среди Unix-поставщиков старой школы.

    17.3. IETF и процесс RFC-стандартизации

    Когда Unix-сообщество соединилось с культурой Internet-инженеров, в него также проникло мышление, сформированное процессом RFC-стандартизации IETF (Internet Engineering Task Force — Инженерная группа по решению конкретной задачи в Internet). Согласно традиции IETF, стандарты должны возникать из опыта с работающим прототипом, но как только они становятся стандартами, код, который им не соответствует, считается нерабочим и безжалостно уничтожается.

    К сожалению, обычно стандарты разрабатываются иначе. История вычислительной техники полна примеров того, как технические стандарты создавались в ходе процесса, объединявшего в себе худшие черты "грубой" философии и темной закулисной политики, что приводило к созданию спецификаций, которые не способны были повторить какие-либо существующие реализации. Еще хуже то, что многие из них были либо настолько требовательными, что практически не могли быть реализованными, либо настолько недоработанными, что создавали больше проблем, чем решали. Затем они пропагандировались среди поставщиков, которые при любом удобном случае игнорировали их, если они создавали неудобства.

    Одним из самых печально известных примеров абсурдной стандартизации были сетевые протоколы Открытого взаимодействия систем (Open Systems Interconnect, OSI), которые недолго конкурировали с TCP/IP в 1980-х годах — семиуровневая модель на расстоянии выглядела изящной, но оказалась чрезмерно сложной и нереализуемой на практике[142]. Другим широко известным негативным примером является стандарт ANSI Х3.64 для средств видеотерминалов, испорченный едва различимой несовместимостью между юридически согласованными реализациями. Даже после того, как символьные терминалы почти были вытеснены растровыми дисплеями, несовместимость продолжала создавать проблемы (в частности, именно поэтому функциональные и специальные клавиши в программе xterm(1) иногда нарушают ее работу). Стандарт RS232 для последовательных соединений был настолько недоопределенным, что иногда казалось, будто не существует двух одинаковых последовательных кабелей. Для описания всех подобных отрицательных примеров стандартов, возможно, потребовалась бы книга такого же объема.

    Существует известная фраза, которая обобщает философию IETF: "Мы отвергаем королей, президентов и голосование. Мы верим в суровое единодушие и работающий код"[143]. Данное требование существования работающей реализации предохранило ее от грубейших ошибок. В действительности критерий еще строже.

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

    (Процесс Internet-стандартизации (The Internet Standards Process) — третья редакция (RFC 2026).)

    Все IETF-стандарты проходят стадию документов RFC (Requests for Comment — запросы на комментарии). Процесс подачи RFC умышленно неформален. RFC-документы могут предлагать стандарты, результаты исследований, формулировать философские обоснования для последующих RFC-документов или даже представлять собой шутки. Появление ежегодного первоапрельского RFC является ближайшим эквивалентом соблюдения "религиозного праздника" среди Internet-хакеров, что привело к возникновению таких перлов, как "A Standard for the Transmission of IP Datagrams on Avian Carriers" (RFC 1149, стандарт для передачи IP-дейтаграмм с голубиной почтой)[144], "The Hyper Text Coffee Pot Control Protocol" (RFC 2324, гипертекстовый протокол управления кофеваркой)[145] и "The Security Flag in the IPv4 Header" (RFC3514, защитный флаг в заголовке IPv4)[146].

    Однако шуточные RFC-документы — единственный вид документов, которые немедленно становятся RFC. Серьезные предложения в действительности начинают свой путь как "Internet-Drafts" (Internet-черновики), которые распространяются для публичного комментирования через IETF-каталоги на нескольких широко известных узлах. Отдельные Internet-черновики не имеют формального статуса и могут быть изменены или удалены их создателями в любое время. Черновики, которые не были отозваны, но и не получили статус RFC, удаляются по истечении шести месяцев.

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

    Internet-черновик, опубликованный с RFC-номером, является спецификацией, с которой могут согласиться разработчики. Ожидается, что авторы RFC-документа и сообщество в целом начнут корректировать данную спецификацию на основании практического опыта.

    Некоторые RFC-документы не продвигаются дальше этой стадии. Спецификация, которая не смогла привлечь внимание и не прошла реальных испытаний, может быть быстро позабыта, и в конечном счете маркируется RFC-редактором как "Not recommended" (не рекомендуется) или "Superseded" (отменено). Неудачные предложения принимаются как издержки процесса, и не считается зазорным быть связанным с таким предложением.

    Управляющий комитет IETF (IESG или Internet Engineering Steering Group — группа технического управления Internet) отвечает за продвижение успешных RFC-документов в виде стандартов путем присвоения им грифа "Proposed Standard" (предложенный стандарт). Для того чтобы RFC квалифицировался как стандарт, спецификация должна быть стабильной, пройти экспертную оценку и привлечь значительный интерес Internet-сообщества. Опыт реализации не является абсолютно необходимым, прежде чем RFC получит обозначение Proposed Standard, но считается весьма желательным, и IESG может его потребовать, если RFC затрагивает основные протоколы или иным образом способен дестабилизировать Internet.

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

    Когда существует как минимум две работающие, полные, независимо созданные и способные к взаимодействию реализации предложенного стандарта, IESG может повысить стандарт до статуса "Draft Standard" (проект стандарта). RFC 2026 гласит: "Повышение до проекта стандарта является главным повышением статуса и подтверждает уверенность в том, что данная спецификация сформировалась и будет полезной".

    RFC-документ, получивший статус Draft Standard, будет изменяться только в целях исправления ошибок логики спецификации. Ожидается, что спецификации, имеющие такой статус, готовы для внедрения в чувствительные к нарушениям среды.

    Когда проект стандарта прошел тест широко распространенной реализации и получил общее одобрение, он может быть назван Internet Standard (Стандарт Internet). Такие стандарты сохраняют свои RFC-номера и, кроме того, получают номер STD-серии. На момент написания данной книги существовало более 3 тыс. RFC и только 60 STD-стандартов.

    RFC, отклоняющиеся от схемы стандартизации, могут быть обозначены как Experimental (экспериментальные), Informational (информационные; шуточные RFC тоже получают такое обозначение) или Historic (имеющие историческое значение). Последнее обозначение применимо к вышедшим из употребления стандартам. Цитата из RFC 2026: "Борцы за чистоту литературного языка предложили использовать слово Historical (исторически сложившийся); однако в данном случае "исторически сложившимся" является использование слова Historic."

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

    17.4. Спецификации — ДНК, код — РНК

    Даже в давние времена PDP-7 Unix-программисты всегда (больше чем их коллеги, работающие с другими системами) были склонны рассматривать старый код как непригодный к повторному использованию. Это, несомненно, было продуктом Unix-традиции, которая акцентировала внимание на модульности, облегчающей выбраковку и замену небольших блоков систем без потери целого. Unix-программисты уяснили из опыта, что попытки сохранения плохого кода или плохого дизайна часто являются более трудоемкими, чем создание проекта заново. Там, где в других культурах программирования инстинкт подсказывал бы исправлять неповоротливые монолиты, потому что в них вложено много труда, инстинкт Unix-программиста обычно ведет к выбрасыванию старого кода и созданию нового.

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

    После 1980 года влияние данного урока стало более заметно в Unix-сообществе. Поэтому, несмотря на то, что стандарт ANSI/ISO С 1989 года не был абсолютно безупречным, он был исключительно четким и практичным для стандарта такого размера и важности. Единая спецификация Unix содержит "атавизмы" трех десятилетий экспериментов и фальстартов в более сложной области и, следовательно, является более беспорядочной, чем ANSI С. Однако компонентные стандарты, из которых она собрана, весьма хороши; твердым доказательством этого является тот факт, что, читая ее, Линус Торвальдс успешно создал Unix с нуля. Скромный, но мощный пример IETF создал один из критически важных блоков среды, которая сделала возможным подвиг Торвальдса.

    Уважение к опубликованным стандартам и процессу IETF глубоко проникло в Unix-культуру. Умышленное нарушение Internet STD-стандартов просто невыполнимо. Это может иногда создавать пропасть взаимного непонимания между людьми с Unix-опытом и другими, склонными предполагать, что наиболее популярная или широко распространенная реализация протокола по определению является верной — даже если она нарушает стандарт так жестко, что не способна взаимодействовать с тщательно согласованным программным обеспечением.

    Для Unix-программиста характерно уважение к опубликованным стандартам, поэтому он, скорее всего, будет враждебно относиться к спецификациям других видов. К тому времени, когда "водопадная модель" (исчерпывающая спецификация, затем реализация, затем отладка без возврата к любой стадии) утратила популярность в литературе по программной инженерии, она уже в течение многих лет была предметом насмешек среди Unix-программистов. Опыт и сильная традиция совместной разработки уже научили их, что лучший способ заключается в создании прототипов и повторных циклах тестирования и развития.

    Традиция Unix четко определяет, что спецификации могут иметь большую ценность, но требует, чтобы они интерпретировались как временные и постоянно пересматривались в процессе практического использования так же, как пересматриваются стандарты Internet-Drafts и Proposed Standards. В лучшей практике Unix документация на программу используется как спецификация, подлежащая пересмотру, аналогично предложенному стандарту Internet.

    В отличие от других сред, в Unix-разработке документация часто пишется до программы или, по крайней мере, совместно с ней. Для X11 основные стандарты X были закончены до выхода первой версии системы X и с тех пор остались по существу неизменными. Совместимость между различными Х-системами совершенствуется далее путем скрупулезного тестирования, управляемого спецификацией.

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

    (Кит Паккард.)

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

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

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

    (Джим Геттис.)

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

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

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

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

    Распознавание таких ошибок является просто вопросом проверки реализации интерфейса относительно спецификации. Естественно, наличие исходного кода для данной реализации несколько упрощает это.

    (Кит Паккард.)

    Позиция предшествующих стандартов имеет свои преимущества также и для конечных пользователей. Тогда как уже не маленькая компания восточнее Белвю имеет проблемы, сохраняя совместимость офисного набора с его предыдущими версиями, GUI-приложения, написанные в 1988 году, до сих пор работают без изменений на нынешних реализациях системы X. В мире Unix подобное долголетие является нормой, а причиной этого является позиция "стандарт как ДНК".

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

    Последнее замечание Кита непосредственно приводит к вопросу, который выдвигается на передний план благодаря успеху Unix-систем с открытым исходным кодом — связь между открытыми стандартами и открытым исходным кодом. Данная проблема рассматривается в конце главы, но перед этим необходимо изучить практический вопрос: каким образом Unix-программисты могут действительно использовать огромную массу накопленных стандартов и как овладеть практическими знаниями для достижения переносимости программного обеспечения?

    17.5. Программирование, обеспечивающее переносимость

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

    Что касается Unix, два десятка лет изучения вопросов определения переносимых API-интерфейсов позволили полностью решить данную проблему. Средства, описанные в Единой спецификации Unix, вероятно, будут присутствовать во всех современных Unix-платформах и вряд ли не будут поддерживаться в будущем.

    Однако не все зависимости от платформы разрешаются с помощью системных или библиотечных API-интерфейсов. Язык реализации также имеет значение; кроме того, проблемы могут быть обусловлены структурой файловой системы и конфигурационными отличиями между исходной и целевой системой. Однако практика Unix уже подтвердила способы решения данных проблем.

    17.5.1. Переносимость и выбор языка

    Первым вопросом в программировании, обеспечивающем переносимость, является выбор языка реализации. Все основные языки, рассмотренные в главе 14, являются высоко переносимыми в том смысле, что совместимые реализации доступны на всех современных Unix-системах, а для большинства из них также доступны реализации для Windows и MacOS. Проблемы переносимости часто возникают не в основных языках, а в библиотеках поддержки и степени интеграции с локальным окружением (особенно в IPC-методах и управлении параллельными процессами, включая инфраструктуру для GUI-интерфейсов).

    17.5.1.1. Переносимость С

    Базовый язык С в высшей степени переносим. Его стандартной реализацией в Unix является GNU C-компилятор, который повсеместно распространен не только Unix-системах с открытым исходным кодом, но и современных коммерческих вариантах операционной системы. GNU С был перенесен на Windows и классическую MacOS, но ни в одной из этих сред не стал широко распространенным, поскольку испытывает недостаток в переносимых привязках к собственным GUI-интерфейсам данных систем.

    Стандартная библиотека ввода/вывода, математические подпрограммы и поддержка интернационализации переносимы во всех реализациях С. Файловый ввод/вывод, сигналы и управление процессами являются переносимыми между вариантами Unix, при условии, что используются только современные API-интерфейсы, описанные в Единой спецификации Unix; более старый C-код часто содержит нагромождения условных операторов препроцессора, направленных на достижение переносимости, но они поддерживают интерфейсы, появившиеся до стандарта POSIX, из старых коммерческих Unix-систем, которые по состоянию на 2003 год устарели или близки к этому.

    Более серьезные проблемы с переносимостью С начинаются в области IPC, параллельных процессов и GUI-интерфейсов. Вопросы переносимости IPC и параллельных процессов обсуждались в главе 7. Реальную практическую проблему создают GUI-инструментарии. Большое количество GUI-инструментариев с открытым исходным кодом универсально переносятся между современными вариантами Unix, а также в Windows и классическую MacOS — Tk, wxWindows, GTK и Qt — четыре хорошо известных вида инструментария с открытым исходным кодом и документацией, которую нетрудной найти в Web. Однако ни один из них не распространяется со всеми платформами, и (по причинам более юридическим, чем техническим) ни один из них не предоставляет на всех платформах естественный вид и восприятие GUI-интерфейса. В главе 15 даны некоторые рекомендации, позволяющие справиться с данной проблемой.

    По теме разработки переносимого C-кода написаны целые тома. Эта книга не входит в их число, и авторы рекомендуют внимательно изучить "Recommended С Style and Coding Standards" [11], а также главу о переносимости из книги "The Practice of Programming" [40].

    17.5.1.2. Переносимость С++

    Для С++ на уровне операционной системы характерны те же проблемы переносимости, что и для С, а также ряд собственных. Одной из дополнительных проблем является то, что GNU компилятор с открытым исходным кодом для С++ значительно отстает от коммерческих реализаций; поэтому к середине 2003 года не существовало универсально внедряемого эквивалента GNU С, на котором можно основывать стандарт де-факто. Более того, ни один из компиляторов С++ не реализовывает полностью ISO-стандарт C++99, хотя GNU С++ подошел к этому очень близко.

    17.5.1.3. Переносимость shell

    Переносимость shell-сценариев, к сожалению, является низкой. Проблема заключается не в самой оболочке; bash(1) (Bourne Again shell с открытым исходным кодом) распространена достаточно широко, для того чтобы малоразвитые shell-сценарии могли выполняться почти в любой среде. Проблема заключается в том, что в большинстве shell-сценариев интенсивно используются другие команды и фильтры, которые являются менее переносимыми, и их присутствие на какой-либо определенной целевой машине никоим образом не гарантируется.

    Данную проблему можно преодолеть героическими усилиями, как в инструментах autoconf(1). Однако это действительно достаточно трудно, и большинство сложнейших случаев программирования, которые обычно реализовались в shell, переместились к языкам сценариев второго поколения, таким как Perl, Python и Tcl.

    17.5.1.4. Переносимость Perl

    Perl отличается хорошей переносимостью. В стандартном варианте языка даже предоставляется переносимый набор привязок к Tk-инструментарию, который поддерживает переносимые GUI-интерфейсы в Unix, MacOS и Windows. Однако этому мешает одна проблема. Perl-сценарии часто требуют добавочных библиотек из архива CPAN (Comprehensive Perl Archive Network — полный сетевой архив Perl), которые не обязательно присутствуют в любой реализации Perl.

    17.5.1.5. Переносимость Python

    Python имеет превосходную переносимость. Как и Perl, стандартный вариант Python предоставляет переносимый набор привязок к Tk-инструментарию, который поддерживает переносимые GUI-интерфейсы в Unix, MacOS и Windows.

    Стандартный Python обладает более развитой стандартной библиотекой, чем Perl и не имеет эквивалента архиву CPAN, на который могли бы полагаться программисты. Вместо этого важные модули расширения регулярно встраиваются в стандартный дистрибутив Python в ходе выпуска второстепенных версий. Это заменяет пространственную проблему временной — Python менее подвержен влиянию эффекта недостающих модулей. За это приходится расплачиваться тем, что второстепенные версии Python отчасти более важны, чем главные версии Perl. На практике этот компромисс благоприятствует Python.

    17.5.1.6. Переносимость Tcl

    Tcl демонстрирует хорошую переносимость в целом, но она сильно различается в зависимости от сложности проекта. Tk-инструментарий для кроссплатформенного GUI-программирования является естественным для Tcl. Как и в случае с Python, развитие основного языка проходит относительно гладко с немногими проблемами перекоса версий. К сожалению, Tcl даже более, чем Perl, зависит от средств расширения, которые не обязательно поставляются с каждой реализацией языка, а кроме того, не существует эквивалента CPAN-архива для централизованного их распространения.

    Таким образом, для мелких проектов, не зависящих от расширений, переносимость Tcl превосходна. Однако крупные проекты часто сильно зависят как от расширений, так и (как в случае с shell-программированием) от вызываемых внешних команд, которые могут отсутствовать на целевой машине; часто они имеют слабую переносимость.

    Парадоксально, но Tcl может страдать от простоты добавления в него расширений. К тому моменту, когда определенное расширение становится интересным в качестве части стандартного дистрибутива, существует, как правило, несколько различных его версий. На симпозиуме разработчиков Tcl/Tk (Tcl/Tk Workshop 1995 года) Джон Аустерхоут (John Ousterhout) объяснил, почему в стандартном дистрибутиве Tcl отсутствует поддержка ОО-средств, так:

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

    17.5.1.7. Переносимость Java

    Переносимость Java превосходна — в конце концов, основной целью создания языка был девиз "написанное однажды работает везде". Вместе с тем переносимость Java не идеальна. Трудности в основном связаны с проблемами перекоса версий между JDK 1.1 и более старым GUI-инструментарием AWT (с одной стороны) и JDK 1.2 и более новым Swing. Это обусловлено несколькими важными причинами.

    • Конструкция AWT Sun была настолько неадекватной, что ее необходимо было заменить инструментарием Swing.

    • Отказ Microsoft от поддержки Java-разработки на Windows и попытка заменить данный язык С#.

    • Решение Microsoft удерживать поддержку аплетов в Internet Explorer на уровне JDK 1.1.

    • Лицензионные положения Sun, которые делают невозможной реализацию JDK 1.2 с открытым исходным кодом, замедляя внедрение пакета (особенно в мире Linux).

    Разрабатывая программы, в которых задействованы GUI-интерфейсы, Java-разработчики, ищущие переносимости, в обозримом будущем окажутся перед лицом выбора: для сохранения максимальной переносимости (включая Microsoft Windows) остановиться на JDK1.1/AWT со слабо спроектированным инструментарием или получить лучший инструментарий и средства JDK 1.2, жертвуя некоторой переносимостью.

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

    17.5.1.8. Переносимость Emacs Lisp

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

    Кроме того, также весьма стабилен набор примитивов Emacs. Он достиг завершенности для задач, которые в течение многих лет возлагались на редактор (манипуляция буферами, обработка текста). Только введение системы X нарушило эту картину, и очень немногие режимы Emacs должны иметь сведения об X. Проблемы переносимости обычно являются проявлением капризов средств операционной системы на C-уровне привязок; управление подчиненными процессами в таких режимах в качестве почтовых агентов — почти единственная область, где такие проблемы проявляются с непредсказуемой частотой.

    17.5.2. Обход системных зависимостей

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

    /var/spool/mail
    или
    /var/mail
    .

    Часто можно избежать этого вида зависимости, отступив и воссоздав проблему. Зачем, так или иначе, открывать файл в каталоге почтового спула? Если запись в него необходима, то, возможно, лучшим решением будет просто вызов локального почтового транспортного клиента, с тем чтобы блокировка файла была выполнена верно. Если выполняется чтение из почтового каталога, то, вероятно, лучше будет обратиться к нему посредством POP3- или IMАР-сервера.

    Подобные вопросы применимы и к другим системным файлам. Не лучше ли, например, вместо ручного открытия журнальных файлов воспользоваться средствами syslog(3). Интерфейсы вызова функций через библиотеку С стандартизированы лучше, чем расположение системных файлов, и этим следует пользоваться.

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

    /var/mail
    и
    /var/spool/mail
    .

    17.5.3. Инструменты, обеспечивающие переносимость

    Часто для разрешения вопросов переносимости, обследования системной конфигурации и настройки make-файлов можно использовать GNU-утилиту с открытым исходным кодом autoconf(1), которая рассматривалась в главе 15. Пользователи, которые сегодня предпочитают компилировать программы из исходного кода, рассчитывают на возможность ввести команды

    configure; make; make install
    и получить чистую сборку. На странице <http://seul.org/docs/autotut/> публикуется хорошее учебное пособие по использованию данных инструментов. Даже если программа распространяется в бинарном виде, инструменты autoconf(1) способны помочь автоматизировать решение проблемы подстройки кода под условия различных платформ.

    Существуют другие инструменты, которые решают данную проблему. Двумя из наиболее широко известных средств являются утилита Imake(1), связанная с системой X Window и инструмент Configure, созданный Ларри Уоллом (Larry Wall, создавшим позднее язык Perl) и приспособленный для многих различных проектов. Все они, как минимум, также сложны как набор autoconf, и используются в настоящее время не чаще. Они не охватывают такой же широкий диапазон целевых систем как autoconf.

    17.6. Интернационализация

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

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

    Наиболее известным инструментом для решения данной задачи является GNU-утилита gettext, которая требует заключать в специальный макрос строки исходного языка, которые необходимо интернационализировать. Макрос использует каждую строку как ключ в словарях всех языков, которые могут поставляться в виде отдельных файлов. Если такие словари не доступны (или доступны, но в процессе поиска соответствие не было найдено), то макрос просто возвращает свой аргумент, безоговорочно отступая к собственному языку в коде.

    Несмотря на то, что сама по себе программа gettext к середине 2003 года являлась хрупкой и беспорядочно организованной, ее общая философия вполне логична. Для многих проектов вполне возможно создать легковесную версию данной идеи с хорошими результатами.

    Во-вторых, в современных Unix-системах прослеживается отчетливая тенденция избавляться от всего "исторического мусора", связанного с множеством таблиц символов, и делать естественным языком приложений UTF-8, 8-битовое кодирование Unicode-символов со смещением (в противоположность, например, использованию в качестве такого языка 16-битовые широкие символы). Нижние 128 символов UTF-8 представляют собой символы ASCII, а нижние 256 символов — Latin-1, что означает обратную совместимость с двумя наиболее широко используемыми таблицами символов. Этому способствует тот факт, что XML и Java сделали данный выбор, но движущая сила уже определена, даже если бы не было XML и Java.

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

    [a-z]
    не обязательно охватывает все строчные буквы, если сценарий или программа разрабатывается, например, для Германии, где острая буква "s" или символ "ß" считается строчным, но не входит в данный диапазон. Подобные проблемы возникают с французскими буквами под ударением. Надежнее использовать элемент
    [[:lower:]]
    и другие символьные диапазоны, описанные в стандарте POSIX.

    17.7. Переносимость, открытые стандарты и открытый исходный код

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

    Рассмотрим, например, разработку программы захвата изображений для цифровой камеры. Зачем создавать собственный формат для сохранения изображений или покупать коммерческий код, когда (как было сказано в главе 5) существует многократно проверенная, полнофункциональная библиотека для записи PNG-изображений, реализованная в открытом исходном коде?

    Создание (или воссоздание) открытого исходного кода также оказало значительное влияние на процесс стандартизации. Несмотря на то, что это не является официальным требованием, IETF приблизительно с 1997 года все больше сопротивляется превращать в стандарты документы RFC, которые не имеют по крайней мере одной эталонной реализации с открытым исходным кодом. В будущем кажется вполне вероятным, что степень соответствия любому определенному стандарту будет все больше измеряться соответствием реализациям с открытым исходным кодом, которые были одобрены авторами стандарта.

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

    (Генри Спенсер.)

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

    До конца 90-х годов данная рекомендация была бы невыполнимой. Несколько альтернатив частных операционных систем и средств разработки были "благородными экспериментами", подтверждениями академических идей или "игрушками". Но Internet изменил все. В середине 2003 года Linux и другие Unix-системы с открытым исходным кодом доказали свою стойкость как платформы для создания программного обеспечения промышленного качества. В настоящее время разработчики имеют лучшую альтернативу, чем зависимость от краткосрочных бизнес- решений, направленных на защиту чужой монополии. Применяйте защищенные конструкции — создавайте программы на основе открытого исходного кода и вы не попадете в безвыходное положение.

    18

    Документация: объяснение кода в Web-сообществе

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

    (—Джозеф Костелло (Josef Costello))

    Первым применением Unix в 1971 году была платформа для компоновки документов — Bell Labs использовала ее для подготовки патентных документов в целях составления картотеки. Фотонабор, управляемый компьютером, все еще оставался тогда новой идеей, и на несколько лет после своего дебюта в 1973 году форматер, который создал Джо Оссана (Joe Ossana), troff(1), определил уровень техники.

    С тех пор сложные форматеры документов, программное обеспечение для набора, а также различные программы макетирования страниц занимают важное место в традициях Unix. Хотя troff(1) доказал свою удивительную долговечность, в Unix также поддерживается значительная работа в данной прикладной области. Сегодня Unix-разработчики и Unix-инструменты находятся на передовом рубеже далеко идущих перемен в практике создания документов, началом которой послужило появление World Wide Web.

    На уровне пользовательских представлений с середины 1990-х годов практика сообщества Unix быстро смещается в направлении идеи "все документы — HTML, все ссылки — URL". Более того, современные программы для просмотра справочной информации в Unix являются просто Web-браузерами, которые способны осуществлять синтаксический анализ определенных видов URL-адресов (например, URL "man:ls(1)" интерпретирует man-страницу ls(1) в HTML-страницу). Это облегчает проблемы, вызванные существованием множества различных форматов для главных документов, но не решает их полностью. Составители документов до сих пор вынуждены решать проблему выбора главного формата, который удовлетворит их специфические потребности.

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

    18.1. Концепции документации

    В первую очередь необходимо определить различия между WYSIWYG-программами (WYSIWYG, "What You See Is What You Get" — "что видишь, то и получаешь") и инструментами разметки (Markup-Centered Tools). Большинство настольных издательских программ и текстовых процессоров входят в первую категорию; они имеют GUI-интерфейсы, в которых вводимый пользователем текст вставляется непосредственно в экранное представление документа, которое задумано как наиболее точное подобие окончательной печатаемой версии. Напротив, в системе, ориентированной на разметку, документ обычно является простым текстовым документом, содержащим явные, видимые управляющие теги и вообще не имеет сходства с ожидаемой выходной версией. Размеченный исходный документ можно модифицировать с помощью обычного текстового редактора, но необходимо передать программе-форматеру, создающей визуализированный вывод для печати или отображения на экране.

    Визуальный интерфейс, или WYSIWYG-стиль, был чрезмерно затратным для ранних компьютеров и использовался редко вплоть до появления в 1984 году персонального компьютера Macintosh. В настоящее время этот стиль полностью доминирует в не-Unix операционных системах. С другой стороны, собственные инструменты форматирования документов в Unix почти все основаны на разметке. Unix-программа troff(1) 1971 года была форматером разметки и, вероятно, является старейшей из подобных программ, которая используется до сих пор.

    Инструменты разметки до сих пор играют значительную роль, поскольку имеющиеся реализации WYSIWYG часто приводят к различным сбоям — некоторые из них поверхностные, а некоторые глубокие. WYSIWYG-процессоры имеют одну общую проблему с GUI-интерфейсами, которая рассматривалась в главе 11. Тот факт, что пользователь может визуально манипулировать любым объектом, часто означает то, что пользователь должен управлять всем. Данная проблема осталась бы, даже если WYSIWIG-соответствие между экранным выводом и распечаткой было бы идеальным, но это почти всегда не так.

    В действительности WYSIWYG-процессоры не являются полностью WYSIWYG. Большинство из них имеют интерфейсы, скрывающие от пользователя различия между экранным представлением и выводом принтера, фактически не устраняя эти различия. Таким образом, нарушается правило наименьшей неожиданности: визуальный аспект подталкивает к использованию программы как печатной машинки, даже если она таковой не является, а входные данные время от времени приводят к неожиданному и нежелательному результату.

    Верно также и то, что WYSIWIG-системы фактически опираются на код разметки, но "делают все возможное", для того чтобы он стал невидимым при обычном использовании программы. Следовательно, нарушается правило прозрачности: пользователю не видна вся разметка, поэтому трудно привести в порядок документы, которые испорчены в результате несоответствующей разметки.

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

    Сегодня в мире, находящемся под влиянием Web и XML, становится общепринятым разграничивать разметку представления и структурную разметку в документах. Первую составляют инструкции о том, как должен выглядеть документ, а вторую — инструкции о том, как он организован и что означает. Такое разграничение не было очевидно понятным или соблюдаемым в ранних Unix-инструментах, однако оно важно для понимания вынуждающих факторов проектирования, которые привели к их современным потомкам.

    Разметка уровня представления хранит всю информацию для форматирования (например, о желаемом расположении пустых мест и изменении шрифтов) в самом документе. В системах структурной разметки документ должен быть соединен с таблицей стилей (stylesheet), которая дает форматеру указания о том, как преобразовывать структурную разметку документа в физическую компоновку. Оба вида разметки, в конечном итоге, управляют внешним видом печатаемого или просматриваемого документа, однако структурная разметка делает это посредством еще одного уровня преобразования, которое оказывается необходимым, если требуется обеспечить хорошие результаты, как для печати документа, так и для его представления в Web-среде.

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

    Наборы макросов troff (

    mm
    ,
    me
    и мой пакет
    ms
    ) были фактически предназначены для того, чтобы увести пользователей от редактирования, ориентированного на форматирование, в направлении редактирования, ориентированного на содержание документов. Идея заключалась в отметке семантических частей и в последующем использовании различных стилевых пакетов, которые "знали бы", следует ли выделять в данном стиле заголовок жирным шрифтом или нет, центрировать его или нет, и так далее. Таким образом, в одной точке был набор макросов, которые пытались имитировать ACM-стиль, и другой набор, имитировавший стиль Physical Review, но использующий базовую разметку
    -ms
    . Все эти макросы не имели успеха у людей, которые сосредотачивались на создании одного документа и управлении его внешним видом, так же как Web-страницы "тонут" в дискуссиях о том, кто должен управлять внешним видом, — автор или читатель. Я часто встречал секретарей, которые использовали команду
    .AU
    (author name — имя автора) только для того, чтобы выделить текст курсивом, заметив, что она выполняет это действие, а затем сталкивались с трудностями при использовании других эффектов команды.

    (Майк Леск.)

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

    18.2. Стиль Unix

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

    18.2.1. Склонность к большим документам

    Средства подготовки документов в Unix всегда были предназначены, главным образом, для разрешения трудностей, связанных с компоновкой крупных и сложных документов. Первоначально такими документами были патентные заявки и техническая документация. Позднее — научные и технические статьи и техническая документация всех видов. В результате большинство Unix-разработчиков научились ценить средства разметки документов. В отличие от PC-пользователей того времени, Unix-культура не была под впечатлением WYSIWYG текстовых процессоров, когда они стали широко доступными в конце 80-х годов и в начале 90-х годов прошлого века. Даже среди молодых современных Unix-хакеров достаточно необычно встретить того, кто действительно отдает предпочтение WYSIWYG-системам.

    Неприятие трудных для понимания двоичных форматов документов и особенно непонятных частных двоичных форматов также сыграло свою роль в отклонении WYSIWYG-инструментов. С другой стороны, Unix-программисты с энтузиазмом ухватились за PostScript (современный стандарт языка для управления графическими принтерами) как только документация на язык стала доступной. Данный стандарт точно вписывается в Unix-традиции узкоспециальных языков. Современные Unix-системы с открытым исходным кодом имеют превосходные инструменты для работы с форматами PostScript и PDF (Portable Document Format).

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

    Привязанность Unix к системам разметки часто высмеивается как предубеждение или характерная черта замкнутых хакеров, но не является ни тем, ни другим. Так же как считающийся "примитивным" CLI-стиль, Unix во многих случаях лучше, чем GUI-интерфейсы, адаптируется к потребностям опытных пользователей, ориентированная на разметку конструкция таких инструментов, как troff(1), подходит для потребностей опытных компоновщиков документов лучше, чем WYSIWYG-программы.

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

    Развитие World Wide Web означало, что способность отображать документы в различных средах (или, по крайней мере, печатать и отображать HTML-документы) стало после 1993 года главной задачей для инструментов документирования. В то же время даже обычные пользователи под влиянием HTML более комфортно стали воспринимать системы, ориентированные на разметку. Это привело непосредственно к росту заинтересованности в структурной разметке и изобретению XML после 1996 года. Внезапно давняя склонность Unix к системам разметки стала восприниматься скорее как перспективная, нежели реакционная.

    Сегодня для Unix характерна наиболее передовая разработка основанных на XML средствах документирования с использованием структурной разметки. Но в то же время Unix-культура еще должна освободиться от прежней традиции систем разметки уровня представления. "Скрипучий, лязгающий, бронированный динозавр", каковым является troff, только частично был вытеснен языками HTML и XML.

    18.2.2. Культурный стиль

    Большая часть документации по программному обеспечению написана "техническими писателями", стремящимися найти наименьший общий знаменатель незнания — "умное написание для неосведомленных". Документация, которая поставляется с Unix-системами, традиционно пишется программистами для коллег. Даже если документация не написана программистами для программистов, то она часто находится под влиянием стиля и формата огромной массы такой документации, поставляемой с Unix-системами.

    Данные различия можно объединить одним замечанием: man-страницы в Unix обычно содержат раздел, который называется "BUGS" (ошибки). В других культурах "технические писатели" пытаются придать продукту лучший вид, минуя известные ошибки или касаясь их вскользь. В культуре операционной системы Unix разработчики беспощадно описывают известные недостатки своего программного обеспечения, а пользователи рассматривают краткий, но информативный раздел "BUGS" как обнадеживающий знак качественной работы. Коммерческие дистрибутивы Unix, нарушившие данное соглашение, либо исключив раздел "BUGS", либо заменив его название более мягкими формулировками "LIMITATIONS" (ограничения) или "ISSUES" (проблемы), или "APPLICATION USAGE" (использование приложения), неизменно приходят в упадок.

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

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

    18.3. Многообразие форматов документации в Unix

    Все основные форматы Unix-документации, кроме самых новых, являются разметками уровня представления, созданными с помощью макропакетов. Ниже они рассматриваются в порядке их возникновения.

    18.3.1. troff и инструментарий Documenter's Workbench

    Структура и инструменты Documenter's Workbench рассматривались в главе 8 как пример интеграции системы, состоящей из нескольких мини-языков. Здесь рассматривается функциональная роль данного инструментария как системы форматирования документов.

    Форматер troff интерпретирует язык разметки уровня представления. Последние реализации данной программы, такие как GNU-проект groff(1), по умолчанию на выходе создают PostScript-представление, хотя существует возможность получать другие формы вывода путем выбора подходящего драйвера. В примере 18.1 приводится несколько troff-кодов, которые можно встретить в исходных текстах документов.

    troff(1) имеет множество других запросов, но большинство из них вряд ли можно увидеть немедленно. Весьма немногие документы написаны на чистом troff. Он поддерживает макросредства, и в нем более или менее широко используются около десятка макропакетов. Среди них наибольшее распространение получил макропакет man(7), который используется для написания man-страниц Unix. См. пример 18.2.

    Две из почти десяти исторических библиотек макросов troff, ms(7) и mm(7) используются до сих пор. В BSD Unix имеется собственный замысловатый расширенный набор макросов, mdoc(7). Все они предназначены для написания технических руководств и длинных документов. Они имеют подобный стиль, но более сложны, чем man-макросы, и ориентированы на создание форматированного вывода.

    Сокращенный вариант troff(1), который называется nroff(1), генерирует вывод для устройств, поддерживающих только моноширинные шрифты, например, для построчно-печатающих устройств и символьных терминалов. Отображаемая для пользователя внутри терминального окна man-страница Unix форматируется с помощью nroff.

    Пример 18.1. Разметка groff(1)

    Это непрерывный текст.

    .\" Комментарии начинаются с обратной косой черты и двойных кавычек.

    .ft В

    Данный текст будет выделен жирным шрифтом.

    .ft R

    Данный текст будет выведен снова стандартным шрифтом (Roman).

    Данные строки снова, как и "Это непрерывный текст", будут

    отформатированы как заполненный абзац.

    .bp

    Запрос bp приводит к созданию новой страницы и разрыву абзаца.

    Эта строка будет частью второго заполненного абзаца.

    .sp 3

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

    .nf

    Запрос nf отключает заполнение абзаца.

    До тех пор, пока оно снова не будет включено запросом fi,

    пустые места и макет страницы будут сохраняться.


    Одно слово данной строки будет выделено \fBbold\fR жирным шрифтом.

    .fi


    Снова включено заполнение абзаца.

    Средства инструментария Documenter's Workbench очень хорошо решают задачи по форматированию технических документов, для которых они и предназначены, именно поэтому они непрерывно используются в течение более чем 30 лет, тогда как производительность компьютеров возросла в тысячи раз. Они создают форматированный текст приемлемого качества на графических принтерах и способны корректно отображать форматированные man-страницы на экране.

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

    Тем не менее, на момент написания книги man-страницы остаются единственной наиболее важной формой Unix-документации.

    Пример 18.2. Разметка man-страниц

    .SH ПРИМЕР РАЗДЕЛА

    Макрос SH начинает раздел, выделяя его заголовок жирным шрифтом.

    Запрос Р начинает новый абзац. Запрос I форматирует

    свой

    аргумент

    .I курсивом.

    .IP *

    Данный запрос начинает абзац с отступом, отмеченным звездочкой.

    Дополнительный текст для первого абзаца списка.

    .TP

    Эта первая строка станет меткой абзаца

    Данная строка будет первой строкой в абзаце, с отступом

    по отношению к метке.


    Пустая строка выше интерпретируется почти так же, как

    разрыв абзаца (фактически как troff-запрос .sp 1).

    .SS Подраздел

    Это текст подраздела.

    18.3.2. TEX

    TEX (произносится как "теХ") представляет собой очень мощную программу подготовки документов, которая, подобно редактору Emacs, возникла за пределами Unix-культуры, но теперь "прижилась" в ней. Она была создана видным компьютерным ученым Дональдом Кнуттом (Donald Knuth), когда ему надоело качество доступной в конце 70-х годов прошлого века типографской разметки текста и особенно форматирования математических выражений.

    TEX, подобно утилите troff(1), является системой, ориентированной на разметку. TEX имеет гораздо более мощный язык запросов, чем troff. Среди прочего, данная программа лучше приспособлена для обработки изображений, точного позиционирования содержимого страницы и интернационализации. особенно хорошо проявляет себя в форматировании математических выражений и является непревзойденным инструментом для решения базовых задач форматирования, таких как кернинг, заполнение строк и расстановка переносов. ТEХ стал стандартным форматом для представления материалов в большинство математических журналов. В настоящее время TEX курируется как программа с открытым исходным кодом рабочей группой Американского математического общества (American Mathematical Society). Программа также широко используется для создания научных документов.

    Как и в случае с troff(1), пользователь обычно не пишет вручную множество чистых TEX-макросов. Вместо этого используются макро-пакеты и различные вспомогательные программы. Один особый макропакет, TEX является почти универсальным, и большинство пользователей, которые говорят о том, что создают документы в TEX, почти всегда фактически имеют в виду написание LATEX-макросов. Как и макропакеты troff, множество запросов LATEX являются полуструктурными.

    Одно важное применение TEX, которое обычно скрыто от пользователя, заключается в том, что другие инструменты обработки документов часто генерируют LATEX-код, преобразуемый впоследствии в PostScript, вместо того, чтобы самостоятельно пытаться выполнять гораздо более трудную работу по созданию PostScript-документа. Такой прием используется в управляющем сценарии xmlto(1), который рассматривался в главе 14 в качестве учебного примера shell-программирования. Аналогично данная проблема решается в инструментарии XML-DocBook, который описывается ниже в настоящей главе.

    TEX имеет более широкий диапазон применений, чем troff(1), и в основном лучшую конструкцию. Во все более связанном с Web мире рассматриваемая программа имеет те же фундаментальные проблемы, что и troff. Ее разметка сильно привязана к уровню представления, и автоматическое создание хороших Web-страниц из исходных текстов TEX затруднено и чревато сбоями.

    Программа TEX никогда не используется для создания Unix-документации и только иногда используется для документирования приложений. Для таких целей достаточно использовать troff. Однако некоторые программные пакеты, которые возникли в академических учреждениях за пределами Unix-сообщества, заимствовали использование TEX как главного формата документации; одним из примеров является язык Python. Как было сказано выше, он также интенсивно используется для создания математических и научных документов и, вероятно, будет доминировать в этой нише еще несколько лет.

    18.3.3. Texinfo

    Texinfo — разметка документации, разработанная Фондом свободного программного обеспечения. Она используется главным образом для документирования GNU-проектов, включая создание документации для таких важнейших инструментов, как Emacs и коллекция GNU-компиляторов.

    Texinfo была первой системой разметки, специально предназначенной для поддержки вывода форматированного текста на бумагу и гипертекстового вывода для просмотра на экране. Однако используемым в ней гипертекстовым форматом был не HTML, а более примитивная разновидность, которая называется "info", первоначально предназначенная для просмотра из Emacs. Для печати документов Texinfo преобразуется в TEX-макрос, а затем из него в PostScript-представление.

    Texinfo-инструменты в настоящее время способны генерировать HTML-документы. Однако качество и полнота данной операции оставляют желать лучшего, а поскольку множество Texinfo-разметки находится на уровне представления, вряд ли эта ситуация улучшится. В середине 2003 года Фонд свободного программного обеспечения еще продолжал работу над эвристическим преобразованием Texinfo в DocBook. Вероятно, формат Texinfo на какое-то время сохранится.

    18.3.4. POD

    POD или Plain Old Documentation (простая старая документация) представляет собой систему разметки, применяемую кураторами проекта Perl. Данная система генерирует страницы руководства и имеет все известные проблемы разметок уровня представления, включая проблемы при создании хороших HTML-документов.

    18.3.5. HTML

    С момента широкого распространения World Wide Web в начале 90-х годов прошлого века документация небольшой, но возрастающей части Unix-проектов пишется непосредственно на HTML. Проблема данного подхода заключается в том, что генерировать из HTML высококачественный типографский вывод трудно. Кроме того, существуют определенные проблемы с индексированием; необходимая для создания индексов информация в HTML отсутствует.

    18.3.6. DocBook

    DocBook представляет собой определение типов SGML- и XML-документов, предназначенное для крупных, сложных технических документов. Данный формат является единственным исключительно структурированным среди форматов разметки, используемых в Unix-сообществе. Инструмент xmlto(1), описанный в главе 14, поддерживает преобразование в HTML, XHTML, PostScript, PDF, разметку справочной системы Windows и несколько менее важных форматов.

    Несколько главных проектов с открытым исходным кодом (включая Linux Documentation Project, FreeBSD, Apache, Samba, GNOME и KDE) уже используют DocBook в качестве главного формата. Данная книга была написана в XML-DocBook.

    Изучение DocBook — большая тема. Ее рассмотрение будет продолжено после обобщения проблем, связанных с современным состоянием подготовки документов в Unix.

    18.4. Современный хаос и возможный выход из положения

    В настоящее время Unix-документация является "неорганизованной смесью".

    Главные файлы документации в современных Unix-системах разбросаны по восьми различным форматам разметки:

    man
    ,
    ms
    ,
    mm
    , TEX, Texinfo, POD, HTML и DocBook. He существует унифицированного способа просматривать все сформированные версии. Они не доступны в Web и не снабжены перекрестными ссылками.

    Многие представители Unix-сообщества осознают данную проблему. Во время написания данной книги большинство усилий, направленных на ее разрешение, исходит от разработчиков открытого исходного кода, которые более активно заинтересованы в конкуренции за предпочтения нетехнических конечных пользователей, чем разработчики частных Unix-систем. С 2000 года практика движется в направлении использования XML-DocBook как формата обмена документацией.

    Ясная, но требующая больших усилий для достижения цель заключается в том, чтобы снабдить каждую Unix-систему программным обеспечением, которые будет функционировать как общесистемный реестр документации. Когда системные администраторы устанавливают пакеты, одним из этапов будет ввод в данный реестр XML-DocBook-документации для этих пакетов. Затем документация будет преобразована в общедоступное дерево HTML-документов и снабжена перекрестными ссылками с уже имеющейся документацией.

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

    Соответственно, далее формат DocBook и соответствующий инструментарий рассматривается более подробно. Данное описание следует читать как введение в XML в контексте Unix, практическое руководство для применения и как основной учебный пример. Оно является хорошим примером того, как в контексте сообщества Unix, вокруг общих стандартов развивается взаимодействие между различными проектными группами разработчиков.

    18.5. DocBook

    Большое количество главных проектов с открытым исходным кодом сходятся в том, что DocBook является стандартным форматом их документации. Сторонники разметки, основанной на XML, видимо, выиграли теоретический спор против разметки уровня представления и в пользу разметки структурного уровня, а эффективный XML-DocBook-инструментарий доступен в виде программ с открытым исходным кодом.

    Тем не менее, DocBook и программы, поддерживающие данный формат, окружает большая неразбериха. Его приверженцы используют непонятный и неприступный даже с точки зрения стандартов компьютерной науки жаргон, привязанный к аббревиатурам, которые не имеют очевидной связи с тем, что необходимо сделать для написания разметки и создания из нее HTML- или PostScript-представления. XML-стандарты и технические документы печально известны своей невразумительностью. В оставшейся части данного раздела предпринимается попытка прояснить жаргон.

    18.5.1. Определения типов документов

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

    DocBook является языком разметки структурного уровня, а именно диалектом XML. DocBook-документ представляет собой блок XML-кода, в котором используются XML-теги для структурной разметки.

    Форматеру для применения к документу таблицы стилей и придания ему соответствующего внешнего вида требуется сведения об общей структуре данного документа. Например, для того чтобы соответствующим образом физически отформатировать заголовки глав, необходимо сообщить форматеру о том, что рукопись книги обычно состоит из титульных элементов, последовательности глав и выходных данных. Для того чтобы сообщить эти сведения, необходимо предоставить форматеру определение типа документа (Document Type Definition — DTD). DTD сообщает форматеру о том, какие элементы могут содержаться в структуре документа и в каком порядке они могут появляться.

    Называя DocBook "диалектом" XML, автор фактически имел в виду, что DocBook является DTD, причем довольно большим DTD, содержащим около 400 тегов[147].

    В тени DocBook работает определенный вид программ, который называется анализатором корректности (validating parser). Первым этапом при форматировании DocBook-документа является его обработка анализатором корректности (клиентская часть DocBook-форматера). Данная программа сравнивает документ с DocBook DTD, для того чтобы гарантировать, что пользователь не нарушает структурных правил DTD (в противном случае серверная часть форматера, т.е. та часть, которая применяет таблицу стилей, может быть поставлена в тупик).

    Анализатор корректности либо перехватывает ошибку, отправляя пользователю сообщение о тех местах, где структура документа нарушена, либо транслирует документ в поток XML-элементов и текста, которые будут скомбинированы сервером с информацией из таблицы стилей для создания отформатированного вывода.

    Данный процесс в целом схематически изображен на рис. 18.1.

    Рис. 18.1. Обработка структурированных документов

    Часть диаграммы внутри пунктирного блока соответствует форматирующему программному обеспечению или инструментальной связке (toolchain). Для того чтобы понять процесс, необходимо кроме очевидного и видимого ввода данных в форматер (исходного текста документа) учитывать два вида скрытого ввода форматера (DTD и таблица стилей).

    18.5.2. Другие DTD-определения

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

    TEI (Text Encoding Initiative, <http://www.tei-c.org/>)— крупное, сложное DTD-определение, которое используется главным образом в академических учреждениях для компьютерной перезаписи литературных текстов. Применяемые в TEI связки Unix-инструментов состоят из множества тех же инструментов, которые задействованы в DocBook, но используются другие таблицы стилей и (естественно) другое DTD-определение.

    XHTML, последняя версия HTML, также является XML-приложением, описанным DTD, что объясняет "родовое сходство" между XHTML- и DocBook-тегами. Инструментальная связка XHTML состоит из Web-браузеров, которые способны форматировать HTML как простой ASCII-текст, а также любого количества узкоспециальных утилит для печати HTML-текста.

    Для того чтобы облегчить обмен структурированной информацией в других областях, таких как биоинформатика и банковское дело, поддерживаются также многие другие XML DTD-определения. Для того чтобы получить представление о доступном многообразии DTD-определений, можно воспользоваться списком репозитариев <http://www.xml.com/pub/rg/DTD_Repositories>.

    18.5.3. Инструментальная связка DocBook

    Обычно для создания XHTML-документа из исходного DocBook-текета используется интерфейсный сценарий xmlto(1). Ниже приводятся примерные команды.

    bash$ xmlto xhtml foo.xml

    bash$ ls *.html

    ar01s02.html ar01s03.html ar01s04.html index.html

    В данном примере XML-DocBook-документ с именем

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

    bash$ xmlto xhtml-nochunks foo.xml

    bash$ ls *.html

    foo.html

    Наконец, ниже приводятся команды для создания PostScript-представления для печати.

    bash$ xmlto ps foo.xml # Создание PostScript

    bash$ ls *.ps

    foo.ps

    Для того чтобы превратить существующие документы в HTML или PostScript, необходимо ядро, которое способно применить к данным документам комбинацию DocBook DTD и подходящей таблицы стилей. На рис. 18.2 показано, как для данной цели совместно используются инструменты с открытым исходным кодом.

    Рис. 18.2. Современная инструментальная связка XML-DocBook

    Синтаксический анализ документа и его трансформация на основе таблицы стилей будет осуществляться одной из трех программ. Вероятнее всего, программой xsltproc, которая представляет собой синтаксический анализатор, поставляемый с Red Hat Linux. Два другие варианта представлены Java-программами Saxon и Xalan.

    Сгенерировать высококачественный XHTML-документ из любого DocBook-текста сравнительно просто. Большую роль в данном случае играет тот факт, что XHTML является просто другим XML DTD-определением. Преобразование в HTML-документ осуществляется путем применения довольно простой таблицы стилей. Данный способ позволяет также просто генерировать RTF-документы, а из XHTML или RTF легко создать простой ASCII-текст.

    Сложный случай представляет собой печать. Создание высококачественного распечатываемого вывода, что на практике означает преобразование в формат Adobe PDF (Portable Document Format), затруднено. Правильное решение данной задачи требует алгоритмического воспроизведения способа мышления наборщика (человека) при переходе от содержания к уровню представления.

    Таким образом, прежде всего, с помощью таблицы стилей структурированная разметка DocBook преобразовывается в другой диалект XML — FO (Formatting Objects — объекты форматирования). FO-разметка является ярко выраженной разметкой уровня представления. Ее можно представить как некоторый функциональный XML-эквивалент troff. Данную разметку необходимо преобразовывать в PostScript для упаковки в PDF.

    В инструментальной связке, поставляемой с Red Hat Linux, данная задача решается с помощью TEX-макропакета, который называется PassiveTeX. Данный макрос преобразовывает объекты форматирования, генерируемые

    xsltproc
    , в язык TEX Дональда Кнутта. Затем вывод TEX, называемый форматом DVI (DeVice Independent), преобразовывается в PDF.

    Сложная цепь XML, TEX-макрос, DVI, PDF — неуклюжая конструкция. Она "гремит, хрипит и имеет уродливые наросты". Шрифты являются значительной проблемой, поскольку XML, TEX и PDF имеют очень разные модели. Кроме того, серьезные трудности связаны с поддержкой интернационализации и локализации. Единственной привлекательной особенностью данной конструкции является то, что она работает.

    Изящным способом разрешения описываемой проблемы будет использование FOP, непосредственного преобразования FO в PostScript, разрабатываемого проектом Apache. С помощью FOP проблема интернационализации даже если и не будет решена полностью, то, по крайней мере, будет определена. XML-инструменты поддерживают Unicode на всем пути к FOP. Преобразование глифов Unicode в PostScript-шрифт является также исключительно проблемой FOP. Единственным недостатком данного подхода является то, что он пока не работает. В середине 2003 года FOP находился в незаконченной альфа-стадии. FOP-преобразование можно использовать, однако оно содержит множество "необработанных углов" и характеризуется недостатком функций.

    На рис. 18.3 иллюстрируется схема инструментальной связки FOP.

    Рис. 18.3. Будущая инструментальная связка XML-DocBook с использованием FOP

    У FOP есть конкурент. Другой проект, который называется xsl-fo-proc, предназначен для решения тех же задач, что и FOP, но написан на С++ (и, следовательно, работает быстрее, чем Java, и не зависит от Java-окружения). В середине 2003 года проект xsl-fo-proc находился в незавершенной альфа-стадии и продвинулся не дальше, чем FOP.

    18.5.4. Средства преобразования

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

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

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

    GNU Texinfo

    Фонд свободного программного обеспечения намеревается поддерживать DocBook как общий формат для обмена информацией. Texinfo имеет достаточную структуру для того, чтобы сделать возможным довольно точное автоматическое преобразование (после преобразования требуются немногочисленные исправления пользователем), а в версиях 4.x makeinfo представлен ключ

    --docbook
    , который генерирует DocBook-документ. Более подробная информация представлена на странице проекта makeinfo <http://www.gnu.org/directory/texinfo.html>.

    POD

    Модуль POD::DocBook, <http://www.cpan.org/modules/by-module/Pod/> преобразовывает разметку POD (Plain Old Documentation) в DocBook. Утверждается, что модуль преобразовывает каждый POD-тег, кроме курсивного тега

    L<>
    . На странице руководства также сказано: "Вложенные списки
    =over/=back
    внутри DocBook не поддерживаются", однако, следует отметить, что модуль интенсивно тестируется.

    LATEX

    Проект, который называется TeX4ht, <http://www.lrz-muenchen.de/services/software/sonstiges/tex4ht/mn.html>, способен, по утверждению автора PassiveTEX, генерировать DocBook из LATEX.

    man-страницы и другая troff-разметка

    Как правило, считается, что преобразование таких документов представляет крупнейшую и труднейшую проблему. Действительно, базовая разметка troff(1) находится на слишком низком уровне представления для инструментов автоматического преобразования. Однако ситуация значительно прояснятся, если рассмотреть преобразование из исходных текстов документов, написанных в таких макропакетах, как man(7). Они имеют достаточно структурных элементов, для того чтобы улучшить автоматическое преобразование.

    Я сам написал инструмент для troff-DocBook преобразования, поскольку не находил другого, который выполнял бы такую работу с приемлемым качеством. Программа называется doclifter, <http://www.catb.org/~esr/doclifter/>. Она транслирует либо в SGML, либо в XML DocBook из макросов man(7), mdoc(7), ms(7) или me(7). Подробности описаны в документации.

    18.5.5. Инструменты редактирования

    К середине 2003 года отсутствовал один компонент — хорошая программа с открытым исходным кодом для редактирования структуры SGML/XML-документов.

    LyX (<http://www.lyx.org/>) представляет собой текстовый процессор с графическим пользовательским интерфейсом, в котором для печати используется LATEX, а также поддерживается структурное редактирование LATEX-разметки. Существует LATEX-пакет, который генерирует DocBook, а также how-to-документ <http://bgu.chez.tiscali.fr/doc/db4lyx/>, описывающий методику написания SGML и XML в LyX GUI.

    GNU TeXMacs <http://www.math.u-psud.fr/~anh/TeXmacs/TeXmacs.html> — проект, направленный на создание хорошего редактора для технических и математических материалов, включая отображаемые формулы. Версия 1.0 была выпущена в апреле 2002 года. Разработчики планируют в будущем включить поддержку XML, но в настоящее время ее пока нет.

    Большинство пользователей до сих пор редактируют DocBook-теги вручную, используя vi или emacs.

    18.5.6. Связанные стандарты и практические приемы

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

    Непосредственно на достижение данной цели направлен проект ScrollKeeper <http://scrollkeeper.sourceforge.net/>. Данный проект предоставляет набор сценариев, которые могут использоваться в правилах инсталляции и деинсталляции пакетов для регистрации и удаления их документации.

    Программа ScrollKeeper использует открытый формат метаданных (Open Metadata Format) <http://www.ibiblio.org/osrt/omf/>. Данный формат является стандартом для индексирования документации по программам с открытым исходным кодом, аналогичный библиотечной карточно-каталоговой системе. Идея заключается в поддержке развитых средств поиска, в которых используются карточно-каталоговые метаданные, а также исходные тексты документации.

    18.5.7. SGML

    В предыдущих разделах умышленно опускалась большая часть истории развития формата DocBook. Язык XML имеет "старшего брата", SGML (Standard Generalized Markup Language — стандартный обобщенный язык разметки).

    До середины 2002 года любая дискуссия о DocBook была бы неполной без длинного экскурса в SGML, объяснения различий между SGML и XML, а также подробного описания инструментальной связки SGML DocBook. Теперь все значительно проще. Инструментальная связка XML DocBook доступна в виде открытого исходного кода, работает так же, как работала связка SGML, и является более простой в использовании.

    18.5.8. Справочные ресурсы по XML-DocBook

    Одним из факторов, который затрудняет изучение DocBook, является то, что сайты, связанные с данной темой, часто перегружают новичков длинными списками W3C-стандартов, массивными упражнениями по SGML-идеологии и абстрактной терминологией. Хорошее общее введение представляет собой книга "XML in a Nutshell" [32].

    Книга Нормана Уолша (Norman Walsh) "DocBook: The Definitive Guide" доступна в печатном виде <http://www.oreilly.com/catalog/docbook/> и в Web — <http://www.docbook.org/tdg/en/html/docbook.html>. Книга представляет собой действительно полный справочник, но в качестве вводного курса или учебного пособия неудобна. Вместо нее рекомендуются материалы, перечисленные ниже.

    Написание документов с помощью DocBook (Writing Documents Using DocBook) <http://xml.Web.cern.ch/XML/goossens/dbatcern/> — превосходное учебное пособие.

    Существует также одинаково полезный список часто задаваемых вопросов (DocBook FAQ), <http://www.dpawson.co.uk/docbook/> с большим количеством материалов по форматированию HTML-вывода. Кроме того, функционирует DocBook-форум (DocBookwiki) <http://docbook.org/wiki/moin.cgi>.

    Наконец, в документе "The XML Cover Pages" <http://xml.coverpages.org/> представлены описания XML-стандартов.

    18.6. Лучшие практические приемы написания Unix-документации

    Рекомендацию, данную в начале главы, можно рассмотреть с противоположной точки зрения. Создавая документацию для пользователей внутри Unix-культуры, не следует "оглуплять" ее. Автор документации, написанной для идиотов, сам будет "списан со счетов" как идиот. "Оглупление" документации резко отличается от создания доступной документации. В первом случае автор документации ленив и опускает важные сведения, тогда как во втором требуется глубокое осмысление и безжалостное редактирование.

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

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

    Если разрабатываемый проект имеет значительные размеры, следует, вероятно, поставлять документацию в трех видах: man-страницы в качестве справочного материала, учебный материал, а также список часто задаваемых вопросов (Frequently Asked Questions — FAQ). Также следует создать Web-сайт, который будет служить центральной точкой распространения продукта (основные принципы взаимодействия с другими разработчиками изложены в главе 19).

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

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

    man-страницы должны быть авторитетными справочниками в традиционном Unix-стиле для обычной Unix-аудитории. Учебные пособия должны быть документацией в развернутой форме для нетехнических пользователей. FAQ-списки должны быть развивающимися ресурсами, которые растут по мере того, как группа поддержки программы выясняет часто задаваемые вопросы и ответы на них.

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

    1. Поддержка главных документов в XML-DocBook. Даже тап-страницы могут быть документами DocBook

    RefEntry
    . Существует очень хорошее методическое руководство (HOWTO) <http://www.linux.doc.org/HOWTO/mini/Man-Page.html> по созданию man-страниц, в котором описана общая организация, ожидаемая пользователями.

    2. Поставка документации с главными XML-файлами. На случай, если системы пользователей не имеют xmlto(1), поставляйте исходные тексты troff, которые можно получить, выполнив команду

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

    3. Создание ScrollKeeper-совместимого инсталляционного пакета проекта.

    4. Создание XHTML-документов из главных документов (с помощью команды

    xmlto xhtml
    ) и предоставление к ним доступа с Web-страницы проекта.

    Независимо от того используется ли в качестве главного формата XML-DocBook, необходимо найти способ конвертирования документации проекта в HTML. Независимо от того, как поставляется разрабатываемое программное обеспечение — как открытый исходный код или как частная программа, — растет вероятность того, что пользователи найдут ее посредством Web. Непосредственный эффект от размещения документации в Web-среде заключается в том, что это упрощает ее чтение и изучение для потенциальных пользователей и клиентов, знающих о существовании программы. Кроме того, возникает косвенный эффект — повышается вероятность появления программы в результатах Web-поиска.

    19

    Открытый исходный код: программирование в новом Unix-сообществе

    Программы как секс — лучше, когда они бесплатны.

    (—Линус Торвальдс)

    Глава 2 заканчивалась формулировкой самого значительного закона в истории Unix. Операционная система Unix "расцветала", когда ее практика наиболее близко приближалась к открытому исходному коду, и приходила в упадок, когда этого не было. Затем в главе 16 утверждалось, что инструменты разработки с открытым исходным кодом часто характеризуются высоким качеством исполнения. Данная глава начинается с общего объяснения того, как и почему действует разработка открытого исходного кода. Большая часть ее поведения является просто усилением общепринятых практических приемов в традиции Unix.

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

    Данные традиции описываются в предположении, что читатели разрабатывают открытый исходный код. Большинство традиций представляют собой хорошие идеи даже при написании частного программного обеспечения. Предположение об открытом исходном коде является исторически целесообразным, поскольку многие из описываемых традиций через вездесущие инструменты с открытым исходным кодом, такие как patch(1), Emacs и GCC, "уходят корнями" в частные лаборатории разработки Unix.

    19.1. Unix и открытый исходный код

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

    Поразительно хорошей она является для тех людей, которые развили в себе ментальную привычку рассматривать секретность процесса и частный контроль как непременное условие. Со времени выхода книги "The Mythical Man-Month" [8] и до возникновения Linux ортодоксальными в программной инженерии были все мелкие, четко управляемые коллективы внутри тяжеловесных организаций, таких как корпорации и правительство. Чаще всего это были строго управляемые крупные коллективы.

    Раннее Unix-сообщество до лишения прав AT&T было образцовым примером открытого исходного кода в действии. Несмотря на то, что код операционной системы Unix до лишения прав AT&T был технически и юридически частным, он рассматривался как общая часть внутри сообщества пользователей и разработчиков. Добровольные усилия координировались людьми, наиболее сильно заинтересованными в решении проблем. Такая возможность выбора привела к возникновению многих полезных последствий. Действительно, методика разработки открытого исходного кода развивалась как неосознанная народная практика в Unix-сообществе более чем четверть века, за много лет до того, как она была проанализирована и получила свое название в конце 1990-х годов (см. "The Cathedral and the Bazaar" [67] и "Understanding Open Source Software Development" [18]).

    Оглядываясь в прошлое, поразительно отмечать, как все мы были склонны забывать последствия нашего поведения. Некоторые подошли весьма близко к пониманию данного феномена. Например, Ричард Габриель (Richard Gabriel) в своей статье "Worse Is Better" [25] в 1990 году, однако прообразы можно найти в книге Брукса [8] (1975 год) и еще более ранних размышлениях Виссотски (Vyssotsky) и Корбати (Corbaty) о конструкции Multics (1965 год). Мне не удавалось это сделать в течение более чем 20 лет наблюдения за разработкой программного обеспечения, пока меня в середине 1990-х годов "не разбудила" Linux. Этот опыт должен заставить любого вдумчивого и сдержанного человека заинтересоваться тем, какие другие важные унифицирующие концепции в нашем поведении до сих пор остаются неявными и проходят незамеченными нашим коллективным разумом, скрытые не из-за своей сложности, а из-за крайней простоты.

    Перечислим весьма простые правила разработки открытого исходного кода.

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

    2. Выпускайте первую версию быстро, выпускайте последующие версии часто. Быстрый темп выхода версий означает быструю и эффективную обратную связь. Когда каждая следующая версия является небольшой, изменение курса в ответ на отзывы реальных пользователей будет проще.

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

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

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

    Следствием второго правила является то, что выпуск отдельных версий не должен быть важным событием, предполагающим большую подготовку и многообещающие перспективы. Важно беспощадно рационализировать процесс выхода новых версий, с тем чтобы свежие версии можно было выпускать безболезненно. Схема, при которой во время подготовки новой версии необходимо прекратить всю остальную работу, крайне неудачна. (Особенно в случае использования системы CVS или подобной ей, подготовительные версии должны "ответвляться" от главной линии разработки, для того чтобы не блокировать основной процесс.) Вывод: не следует трактовать выход версии как большое, особое событие; сделайте его частью установившегося процесса.

    (Генри Спенсер.)

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

    В разработке открытого исходного кода задействованы большие коллективы программистов, рассеянных по всему миру и общающихся главным образом с помощью электронной почты и Web-страниц. Как правило, большинство соавторов в любом проекте являются добровольцами, вносящими свой вклад ради повышения полезности программы для них самих. Управляет проектом главный разработчик или центральная группа. Другие соавторы могут время от времени подключаться к проекту и выбывать из него. Для того чтобы поощрять соавторов-любителей, важно не допустить появления социальных барьеров между ними и основным коллективом. Необходимо минимизировать привилегированный статус основного коллектива и упорно работать над тем, чтобы сохранить границы незаметными.

    Проекты с открытым исходным кодом следуют традиционному для Unix-сообщества совету автоматизировать работу при любой возможности. Разработчики используют утилиту patch(1) для распространения последовательных изменений. Во многих проектах (и во всех крупных) имеются доступные по сети репозитории кода, использующие системы контроля версий, подобные CVS (эта тема подробно рассматривается в главе 15). Использование автоматизированных систем отслеживания ошибок и исправлений также является широко распространенной практикой.

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

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

    19.2. Лучшие практические приемы при взаимодействии с разработчиками открытого исходного кода

    Основной составляющей лучшей практики в сообществе открытого исходного кода является естественная адаптация к распределенной разработке. В оставшейся части данной главы представлены сведения о поведении, при котором поддерживается хорошая связь с другими разработчиками. Там, где соглашения Unix являются условными (такие как стандартные имена файлов, передающие метаинформацию об исходном дистрибутиве), часто прослеживается их происхождение либо из Usenet (начало 1980-х годов), либо из соглашений и стандартов проекта GNU.

    19.2.1. Хорошая практика обмена исправлениями

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

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

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

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

    19.2.1.1. Отправляйте заплаты, а не целые архивы или файлы

    Если изменения включают в себя новый файл, который отсутствует в коде, то, естественно, приходится отправлять данный файл целиком. Однако если изменяются только уже существующие файлы, отправлять их полностью не следует. Вместо этого следует отправить diff-файл, а именно вывод команды diff(1), выполненной для сравнения основной распространяемой версии с модифицированной.

    Команда diff(1) и обратная ей команда patch(1) являются основными инструментами разработки открытого исходного кода. Diff-файлы лучше, чем целые файлы, поскольку разработчик, которому отсылается заплата, мог изменить основной код с момента получения копии создателем заплаты. Отправляя разработчику diff-файл, создатель заплаты предотвращает дополнительную работу по разделению изменений, которую придется выполнить разработчику, и демонстрирует таким образом, что он ценит его время.

    19.2.1.2. Отправляйте исправления к текущей версии кода

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

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

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

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

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

    configure
    создателя заплаты и куратора. Такой файл создается с помощью
    autoconf
    .

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

    19.2.1.4. Не отправляйте заплат, которые только убирают $-идентификаторы систем RCS или SCCS

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

    $Id$
    , используемые в системах RCS и CVS.

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

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

    19.2.1.5. Используйте вместо формата по умолчанию (
    -e
    ) форматы
    или
    -u

    Принятый по умолчанию в diff(1) формат

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

    Получение diff-файлов, созданных с ключом

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

    19.2.1.6. Сопровождайте заплаты документацией

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

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

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

    19.2.1.7. Сопровождайте заплату пояснениями

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

    Записка может быть краткой, фактически некоторые из наиболее эффективных пояснительных записок, которые встречались автору, содержали в себе только одну мысль: "См. обновления документации в данной заплате". Однако записка должна демонстрировать правильное отношение к работе.

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

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

    "Исправил ошибки по дампу, которые могут возникать, когда какой-либо foo-ввод является слишком длинным. В ходе решения проблемы я искал подобные переполнения в других местах. Возможная причина переполнения находится в файле blarg.c, где-то около строки 666. Вы уверены, что отправитель не может сгенерировать более 80 символов в течение одной передачи?"

    "Вы рассматривали использование Foonly-алгоритма для решения данной проблемы? Здесь есть пример хорошей реализации — <http://www.example.com/~jsmith/foonly.html>."

    "Данная заплата решает первоочередную проблему, но я понимаю, что она неприятно усложняет распределение памяти. У меня заплата работает, однако перед распространением ее, вероятно, следует протестировать под большой нагрузкой."

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

    19.2.1.8. Включайте в код полезные комментарии

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

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

    Ниже приводится пример плохого комментария.

    /* норман ньюби исправил этот код 13 августа 2001 года */

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

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

    /*

    * Необходимо защитить этот переход, так чтобы в crunch_data()

    * никогда не передавался NULL-указатель.

     <norman_newbie@foosite.com>

    */

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

    19.2.1.9. Не огорчайтесь, если заплата отклонена

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

    19.2.2. Хорошая практика наименования проектов и архивов

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

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

    19.2.2.1. Используйте GNU-стиль названий с именной частью и номерами (основной.второстепенный.заплата)

    Будет хорошо, если архивные файлы проекта будут иметь GNU-подобные названия — именной префикс, состоящий из строчных алфавитно-цифровых символов, дефис, номер версии, расширение и другие суффиксы.

    Хорошая общая форма названия содержит следующие части в указанном порядке.

    1. Префикс проекта.

    2. Дефис.

    3. Номер версии.

    4. Точка.

    5. "src" или "bin" (не обязательно).

    6. Точка или дефис (точка предпочтительнее).

    7. Тип бинарных файлов и параметры (не обязательно).

    8. Расширения архивирования и компрессии.

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

    Предположим, существует проект, который называется "foobar", основной номер версии (major version) 1, второстепенный номер версии (minor version) или выпуска 2, уровень исправлений (patchlevel) 3. Ниже представлены примеры названий для проекта, содержащего только один архив (предположительно исходный код).

    foobar-1.2.3.tar.gz

    Архив исходного кода.

    foobar.lsm

    LSM-файл (для публикации в ibiblio).

    Не следует использовать имена, подобные приведенным ниже.

    foobar123.tar.gz

    Многие программы распознают такое имя как архив для проекта, который называется "foobar123" без номера версии.

    foobar1.2.3.tar.gz

    Многие программы распознают такое имя как архив для проекта, который называется "foobar1" версии 2.3.

    foobar-v1.2.3.tar.gz

    Многие программы распознают такое имя как проект, который называется "foobar-v1".

    foo_bar-1.2.3.tar.gz

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

    FooBar-1.2.3.tar.gz

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

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

    foobar-1.2.3.src.tar.gz

    Исходный код.

    foobar-1.2.3.bin.tar.gz

    Бинарные файлы без указания типа.

    foobar-1.2.3.bin.i386.tar.gz

    Бинарные файлы для архитектуры i386.

    foobar-1.2.3.bin.i386.static.tar.gz

    Статически связанные бинарные файлы для архитектуры i386.

    foobar-1.2.3.bin.SPARC.tar.gz

    Бинарный код для архитектуры SPARC.

    Не используйте такие имена, как "foobar-i386-1.2.3.tar.gz", поскольку программы испытывают затруднения с выделением инфиксов типа (например, "-i386") из именной части.

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

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

    В некоторых проектах и сообществах имеются четкие соглашения для имен и номеров версий, которые не обязательно согласуются с приведенными выше рекомендациями. Например, модули Apache, как правило, получают такие имена, как "modfoo", и имеют номер собственной версии и номер версии Apache, с которой они работают. Подобным образом, модули Perl имеют номера версий, которые можно интерпретировать как числа с плавающей точкой (например, можно встретить версию 1.303, а не 1.3.3), а дистрибутивы обычно получают такие имена, как "Foo-Bar-1.303.tar.gz", для версии 1.303 модуля Foo::Bar. (Сам проект Perl, с другой стороны, перешел на использование описанных здесь соглашений в конце 1999 года.)

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

    19.2.2.3. Упорно ищите уникальный префикс имени, который легко вводить

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

    Два различных проекта, имеющих одинаковые именные части, сбивают людей с толку. Поэтому, прежде чем выпускать первую версию, необходимо проверить имя на предмет конфликтов. Для такой проверки можно использовать индексный файл проекта ibiblio <http://metalab.unc.edu/pub/Linux> и индекс приложений на сайте Freshmeat <http://www.freshmeat.net>. Хорошим ресурсом для проверки также является сайт проекта SourceForge <http://www.sourceforge.net>.

    19.2.3. Хорошая практика разработки

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

    19.2.3.1. Не полагайтесь на частный код

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

    19.2.3.2. Используйте автоинструменты GNU

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

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

    configure; make; make install
    и получить чистую сборку, и это действительно так. Полезные учебные материалы по работе с данными инструментами приведены на странице <http://seul.org/docs/autotut>.

    Зрелыми утилитами являются autoconf и autoheader. Программа automake, как отмечалось ранее, еще до середины 2003 года была нестабильной и содержала много ошибок. Возможно, понадобится поддерживать собственный файл

    Makefile.in
    . К счастью, automake является наименее важной программой в наборе автоинструментов.

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

    Однако утилиту autoconf нельзя рассматривать как одобрение кнопочных конструкций. Если вообще возможно, при программировании необходимо придерживаться стандартов, подобных POSIX, и воздерживаться от опроса системы для получения конфигурационной информации. Рекомендуется сохранять минимальное число ifdefs-директив, а еще лучше — не иметь их вообще.

    19.2.3.3. Тестируйте код перед выпуском версии

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

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

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

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

    make test
    .

    19.2.3.4. Выполняйте контроль ошибок в коде перед выпуском версии

    Под "контролем ошибок" (sanity check) здесь подразумевается использование всех доступных инструментов, обладающих приемлемой способностью к обнаружению ошибок, которые человек склонен пропускать. Чем больше таких ошибок обнаружат данные инструменты, тем меньше пользователям и самому разработчику придется с ними бороться.

    При написании программ на C/C++ с использованием GCC рекомендуется выполнять тестовую компиляцию с параметром -Wall и устранять все ошибки перед каждым выходом новой версии. Кроме того, стоит компилировать код всеми доступными компиляторами — разные компиляторы часто обнаруживают различные проблемы. В частности, скомпилируйте программу на машине с действительно 64-битовой архитектурой. Базовые типы данных могут изменяться на 64-битовых машинах, и поэтому в них часто обнаруживаются новые проблемы. Найдите систему Unix-поставщика и запустите утилиту lint для проверки программы.

    Используйте инструменты, которые ищут утечки памяти и другие ошибки времени выполнения. Программы Electric Fence и Valgrind — хорошие инструменты, доступные в виде открытого исходного кода.

    Для Python-проектов полезным инструментом проверки может оказаться программа PyChecker <http://sourceforge.net/projects/pychecker>. Она часто обнаруживает нетривиальные ошибки.

    При написании программ на Perl проверять код следует с помощью ключа

    (и возможно
    -T
    , если он применим). Используйте ключ
    -w
    и конструкции "
    use strict
    ". (Дальнейшую информацию можно найти в документации на Perl.)

    19.2.3.5. Проверяйте орфографию в документации и README-файлах перед выпуском версии

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

    19.2.3.6. Рекомендованные практические приемы переносимости кода C/C++

    При написании программ на С используйте полные ANSI-функции. В частности, используйте прототипы функций, которые помогают выявить несовместимость между модулями. Старые компиляторы в стиле K&R — древняя история.

    Не полагайтесь на специфические для некоторых компиляторов функции, такие как GCC-параметр

    -pipe
    или вложенные функции. Они впоследствии повлияют на чужие порты в не-Linux и He-GCC-системе.

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

    Уровень переносимости представляет собой библиотеку (или, возможно, просто набор макросов в заголовочных файлах), которая извлекает только части API-интерфейсов операционных систем, в которых нуждается разрабатываемая программа. Уровни переносимости облегчают создание версий программы для других платформ. Часто никто из членов коллектива разработчиков не знает целевую платформу (например, существуют буквально сотни различных встроенных операционных систем, и никто незнаком со значительной частью этих платформ). Создание отдельного уровня переносимости предоставляет специалисту, знающему целевую платформу, возможность переносить программу без необходимости понимания чего-либо за пределами уровня переносимости.

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

    __file_exists
    вместо вызова stat(2)) позволяет импортировать из системы только ограниченную, необходимую функциональность, упрощая код приложения.

    Рекомендуется всегда писать уровень переносимости на основе функции, а не на основе платформы. Попытки создания отдельных уровней переносимости для каждой поддерживаемой платформы приводят к многочисленным проблемам обновления и обслуживания. "Платформа" всегда выбирается на основе по крайней мере двух параметров: компилятор и версия библиотеки/операционной системы. В некоторых случаях используются три фактора, как когда Linux-поставщики выбирают библиотеку С независимо от версии операционной системы. В случае с M-поставщиками, N-компиляторами и O-версиями операционных систем, количество платформ быстро превышает пределы досягаемости любых коллективов разработчиков, кроме крупнейших. С другой стороны, при использовании стандартов языков и систем, таких как ANSI и POSIX 1003.1, набор функций является относительно ограниченным.

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

    gettimeofday
    ,
    clock_gettime
    ,
    ftime
    или
    time
    для определения текущего времени суток).

    За пределами уровня переносимости необходимо придерживаться следующей рекомендации.

    Директивы

    #ifdef
    и
    #if
    являются последним средством, обычно признаком неудачного воображения, чрезмерной дифференциации продукта, неоправданной "оптимизации" или накопленного мусора. Их использование в середине кода — бедствие. Образцовый пример —
    /usr/include/stdio.h
    из GNU.

    (Дуг Макилрой.)

    Использование директив

    #ifdef
    и
    #if
    допустимо (если хорошо контролируется) внутри уровня переносимости. За его пределами необходимо упорно пытаться заключить их в обусловленные #includes, исходя из символов функций.

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

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

    19.2.4. Хорошая практика создания дистрибутивов

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

    19.2.4.1. Убедитесь, что архивы всегда распаковываются в один новый каталог

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

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

    fоо-0.23.tar.gz
    распаковывается в подкаталог
    fоо-0.23
    .

    В примере 19.1 демонстрируется решение для make-файла, которое позволяет реализовать указанный принцип в предположении, что каталог дистрибутива называется "foobar", a SRC содержит список файлов дистрибутива.

    Пример 19.1. make-правила для tar-архива

    foobar-$(VERS) .tar.gz:

     @ls $(SRC) | sed s:^:foobar-$(VERS)/: >MANIFEST

     @(cd ..; ln -s foobar foobar-$(VERS))

     (cd ..; tar -czvf foobar/foobar-$(VERS).tar.gz `cat foobar /MANIFEST`)

     @(cd ..; rm foobar-$(VERS))

    19.2.4.2. Включайте в дистрибутив README-файл

    В дистрибутив программы следует включать файл

    README
    , который является путеводителем по дистрибутиву. Согласно давнему соглашению (созданному самим Деннисом Ритчи до 1980 года, и распространенному в Usenet в начале 1980-х годов), данный файл является первым файлом, который будут читать бесстрашные исследователи, после того как распакуют исходный код.

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

    1. Краткое описание проекта.

    2. Ссылка на Web-сайт проекта (если он существует).

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

    4. Схема проекта, описывающая важные файлы и подкаталоги.

    5. Инструкции по компиляции/установке или ссылка на файл, содержащий такие инструкции (обычно

    INSTALL
    ).

    6. Список кураторов/благодарностей или ссылка на содержащий его файл (обычно

    CREDITS
    ).

    7. Последние новости проекта или ссылка на содержащий их файл (обычно

    NEWS
    ).

    8. Адреса списков рассылки проекта.

    В свое время данный файл обычно назывался

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

    19.2.4.3. Придерживайтесь стандартной практики именования файлов

    Еще до просмотра

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

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

    README

    Файл-путеводитель, который прочитывают первым.

    INSTALL

    Инструкции по конфигурированию, компиляции и установке.

    AUTHORS

    Перечень участников проекта (GNU-соглашение).

    NEWS

    Последние новости проекта.

    HISTORY

    История проекта.

    CHANGES

    Перечень значительных изменений между различными редакциями.

    COPYING

    Лицензионные условия проекта (GNU-соглашение).

    LICENSE

    Лицензионные условия проекта.

    FAQ

    Текстовый документ с перечнем часто задаваемых вопросов по проекту.

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

    README
    было давно разработано Фондом свободного программного обеспечения.

    Наличие файла

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

    Весьма ценным является наличие файлов

    HISTORY
    или
    ы
    , содержащих временные метки для каждой версии. Кроме всего прочего, такие файлы могут помочь доказать существование предшествующей разработки, если разработчику когда-либо придется столкнуться с судебным процессом по нарушению патентов (такое еще не случалось, но лучше быть готовым).

    19.2.4.4. Проектирование с учетом обновлений

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

    Проекты Emacs, Python и Qt имеют хорошее соглашение для реализации этого принципа: каталоги с номерами версий (другой практический прием, который, вероятно, становится установившейся практикой благодаря FSF). Ниже показано, как выглядит установленная библиотека Qt (

    ${ver}
    — номер версии).

    /usr/lib/qt /usr/lib/qt-${ver}

    /usr/lib/qt-${ver}/bin     # Расположение moc

    /usr/lib/qt-${ver}/lib     # Расположение .so

    /usr/lib/qt-${ver}/include # Расположение заголовочных файлов

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

    19.2.4.5. В Linux создавайте RPM-пакеты

    Де-факто стандартным форматом для устанавливаемых бинарных пакетов в Linux является формат, используемый диспетчером пакетов Red Hat Linux, RPM (Red Hat Package manager). Он имеется в большинстве популярных дистрибутивов Linux и поддерживается фактически всеми остальными дистрибутивами (кроме Debian и Slackware; а в Debian можно устанавливать программы из RPM-пакетов). Следовательно, хорошей идеей для сайта проекта будет предоставление устанавливаемых RPM-пакетов наряду с архивами исходных кодов.

    Также хорошей идеей будет включение в архив с исходным кодом файл RPM-спецификации, из которого правило в

    makefile
    позволяет создавать RPM-пакеты. Файл спецификации должен иметь расширение
    .spec
    ; по данному расширению команда
    rpm -t
    обнаруживает его в архиве.

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

    makefile
    или
    version.h
    проекта.

    Следует отметить, что в случае поставки исходных RPM-пакетов необходимо использовать тег BuildRoot, для того чтобы программа компилировалась в каталоге /tmp или /var/tmp. Если этого не сделать, то во время выполнения инсталляционной части компиляции файлы будут установлены в действительно конечный каталог. Это произойдет, даже если обнаружатся файловые коллизии, а также если пользователь вообще не хочет инсталлировать пакет. После завершения процедуры инсталляции файлы будут установлены, но в системной базе данных RPM сведения о них не появятся. Такие неверно работающие SRPM-пакеты являются "минным полем" и их следует избегать.

    19.2.4.6. Предоставляйте контрольные суммы пакетов

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

    Хотя существует несколько команд, которые можно использовать с данной целью (такие как

    sum
    и
    cksum
    ), лучше применить криптографически безопасную хэш-функцию. Пакет GPG предоставляет данную возможность посредством параметра
    --detach-sign
    ; аналогичную работу выполняет GNU-команда
    md5sum
    .

    Для каждого поставляемого бинарного пакета Web-сайт проекта должен содержать контрольную сумму и команду, которая требуется для ее генерации.

    19.2.5. Практические приемы хорошей коммуникации

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

    19.2.5.1. Публикация на сайте Freshmeat

    Проект можно анонсировать на сайте Freshmeat <http://www.freshmeat.net>. Кроме того что данный сайт читают широкие круги заинтересованных лиц, группа проекта является крупным источником информации для Web-каналов технических новостей.

    Не надейтесь, что аудитория читает объявления о выходе новых версий программы с момента ее появления. Всегда включайте как минимум одну строку, описывающую функции программы. Пример неудачного объявления: "Announcing the latest release of FooEditor, now with themes and ten times faster" (Анонсируется последняя версия FooEditor, теперь с темами и работает в десять раз быстрее). Удачное объявление: "Announcing the latest release of FooEditor, the scriptable editor for touch-typists, now with themes and ten times faster" (Анонсируется последняя версия FooEditor, редактора для профессиональных наборщиков, который можно использовать в сценариях, теперь с темами и работает в десять раз быстрее).

    19.2.5.2. Публикация в соответствующих группах новостей

    Найдите группу новостей Usenet, непосредственно относящуюся к разработанному приложению, и анонсируйте в ней проект. Анонсировать проект следует только там, где функция кода будет уместной, и проявлять при этом сдержанность.

    Если (например) выпускается версия программы, написанной на Perl, которая опрашивает IMAP-серверы, то определенно о программе следует объявить в группе

    comp.mail.imap
    . Однако, вероятно, не следует отправлять объявление в группу
    comp.lang.perl
    , если программа не является также полезным примером передовых Perl-технологий.

    Объявление должно включать в себя URL-адрес Web-сайта проекта.

    19.2.5.3. Создайте Web-сайт

    Если разработчик намеревается создать вокруг своего проекта прочное сообщество пользователей или разработчиков, то должен существовать Web-сайт проекта. Рассмотрим стандартные разделы сайта проекта.

    • Хартия проекта (почему он существует, целевая аудитория и др.).

    • Ссылки для загрузки исходного кода проекта.

    • Инструкции о том, как подключиться к списку (или спискам) рассылки проекта.

    • FAQ (список часто задаваемых вопросов и ответов на них).

    • HTML-версии документации проекта.

    • Ссылки на родственные и/или конкурирующие проекты.

    Примеры хорошо организованных сайтов проектов приведены в главе 16.

    Простым способом создания Web-сайта является размещение проекта на одном из узлов, который специализируется на предоставлении бесплатного хостинга. В 2003 году двумя наиболее важными из таких узлов были SourceForge (который является демонстрационным и тестовым сайтом для частного сотрудничества) и Savannah (поддерживающий проекты с открытым исходным кодом на идеологической основе).

    19.2.5.4. Поддерживайте списки рассылки проекта

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

    Если проект называется "foo", то список рассылки для разработчиков может называться

    foo-dev
    или
    foo-friends
    .

    Важным решением является то, насколько закрытым будет "частный" список рассылки для разработчиков. Широкое участие в обсуждении конструкции проекта часто является полезным, однако если список относительно открыт, то рано или поздно будут возникать вопросы пользователей-новичков. Способов разрешения данной проблемы множество. Требование в документации, относящееся к новым пользователям, не задавать элементарных вопросов, не решит проблему. Так или иначе, необходимо подкреплять данное требование.

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

    19.2.5.5. Публикуйте проект в главных архивах

    Отдельные крупные архивные сайты для программ с открытым исходным кодом перечислены в разделе "Поиск открытого исходного кода" главы 16. Рекомендуется размещать в них свои программы.

    Ниже приводится два других важных узла.

    • Сайт Сообщества Python-программистов <http://www.python.org> (Python Software Activity, для программного обеспечения, написанного на языке Python).

    • CPAN или Comprehensive Perl Archive Network (полный сетевой архив Perl-программ) <http://language.perl.com/CPAN> (для программ, написанных на Perl).

    19.3. Логика лицензирования: как выбрать лицензию

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

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

    Placed in public domain by J. Random Hacker, 2003. Share

    and enjoy! (Всеобщее достояние, Дж. Рэндом Хакер, 2003.

    Используйте и наслаждайтесь!)

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

    Однако существует очень немного программного обеспечения с открытым исходным кодом, которое является всеобщим достоянием. Некоторые разработчики открытого исходного кода хотят использовать право собственности на код, чтобы гарантировать, что программа остается открытой (они склоняются к принятию лицензии GPL). Другие просто хотят контролировать свою правовую незащищенность; одним из пунктов, который входит в состав всех лицензий открытого исходного кода, является отказ от гарантии.

    19.4. Почему следует использовать стандартную лицензию

    Широко известные лицензии, которые согласуются с Определением открытого исходного кода (Open Source Definition), имеют твердо установившиеся "толковательные" традиции. Разработчики (и пользователи, в той мере, как это их интересует) хорошо знают, что именно они подразумевают, и имеют обоснованное мнение о риске и компромиссах. Поэтому, если возможно, следует использовать одну из стандартных лицензий, опубликованных на сайте OSI.

    Если требуется написать собственную лицензию, то ее следует сертифицировать в OSI. Это позволит избежать множества споров и издержек. Те, кто не сталкивался с лицензионными спорами, не предполагают, насколько отвратительными они могут быть; люди становятся необузданными, потому что лицензии считаются почти священными обязательствами, которые затрагивают фундаментальные ценности сообщества открытого исходного кода.

    Более того, присутствие прочной "толковательной" традиции может оказаться важным, если лицензия когда-нибудь будет проверяться в суде. Во время написания данной книги (середина 2003 года) не существовало прецедентного права поддержки или аннулирования какой-либо лицензии открытого исходного кода. Однако существует юридическая система (по крайней мере, в Соединенных Штатах и, возможно, в других странах общего права, таких как Англия и остальная часть Британского государства), предполагающая, что суды интерпретируют лицензии и контракты согласно ожиданиям и практике сообщества, в котором они возникли. Таким образом, имеется веская причина надеяться, что практика сообщества открытого исходного кода будет определяющей, когда судебная система наконец будет вынуждена вступить в действие.

    19.5. Многообразие лицензий на открытый исходный код

    19.5.1. Лицензия MIT или Консорциума X

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

    Шаблон стандартной лицензии Консорциума X можно найти на сайте OSI <http://www.opensource.org/licenses/mit-license.html>.

    19.5.2. Классическая BSD-лицензия

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

    Оригинальная BSD-лицензия — наиболее известная лицензия данного вида. Среди частей культуры свободного программного обеспечения, которые происходят из BSD Unix, данная лицензия используется даже во многих свободных программах, написанных за тысячи миль от Беркли.

    Нередко встречаются также второстепенные варианты BSD-лицензии, которые изменяют правообладателя и опускают требования о рекламе (что фактически делает данную лицензию эквивалентной лицензии MIT). Следует отметить, что в середине 1999 года Комитет по экспорту технологий (Office of Technology Transfer) Калифорнийского университета аннулировал статью о рекламе в BSD-лицензии. Поэтому лицензия на программное обеспечение BSD была облегчена именно таким способом. В случае использования BSD-подхода настоятельно рекомендуется применять новую лицензию (без статьи о рекламе). Данное ограничение было исключено, так как оно приводило к значительным юридическим и процедурным сложностям при определении составляющих рекламы.

    Шаблон BSD-лицензии можно найти на сайте OSI <http://www.opensource.org/licenses/bsd-license.html>.

    19.5.3. Артистическая лицензия

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

    Лицензией такого вида является Артистическая лицензия (Artistic License), которая была разработана для Perl и широко используется в сообществе Perl-разработчиков. Она требует, чтобы модифицированные файлы содержали "заметное уведомление" (prominent notice) о том, что они были изменены. Кроме того, лицензия требует, чтобы люди, распространяющие изменения, предоставили к ним свободный доступ и приложили все усилия для их передачи сообществу свободного программного обеспечения.

    Копия Артистической лицензии доступна на странице <http://www.opensource.org/licenses/artistic-license.html>.

    19.5.4. General Public License

    Общедоступная лицензия GNU (GPL и ее производные: Library (библиотечная) или "Lesser" (облегченная) GPL) является единственной наиболее широко используемой лицензией на свободное программное обеспечение. Как и Артистическая лицензия, она позволяет распространение модифицированного исходного кода при условии, что измененные файлы содержат "заметное уведомление".

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

    Данные дополнительные требования фактически делают GPL более ограничивающей, чем любая другая из широко используемых лицензий. (Ларри Уолл (Larry Wall) разработал Артистическую лицензию, для того чтобы избежать данных ограничений, одновременно добиваясь множества тех же целей.)

    Ссылка на GPL и инструкции по ее применению приведены на сайте авторского права FSF <http://www.gnu.org/copyleft.html>.

    19.5.5. Mozilla Public License

    Mozilla Public License (Общественная лицензия Mozilla) поддерживает программное обеспечение, которое поставляется с открытым исходным кодом, но может быть связано с закрытыми модулями или расширениями. Она требует, чтобы распространяемое программное обеспечение ("Covered Code" — защищенный код) оставалось открытым, но запрещает оставлять закрытыми расширения, вызываемые через определенный API-интерфейс.

    Шаблон для MPL находится на сайте OSI <http://www.opensource.org/licenses/MPL-1.1.html>.

    20

    Будущее: опасности и перспективы

    Наилучший путь предсказать будущее — создать его.

    (Фраза на собрании в XEROX PARC в 1971 году) (—Алан Кей (Alan Key))

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

    20.1. Сущность и случайность в традиции Unix

    Для того чтобы понять, как проектирование в Unix может измениться в будущем, следует начать с рассмотрения того, как стиль Unix программирования изменялся со временем в прошлом. Эта попытка приводит непосредственно к одной из трудностей в понимании Unix-стиля — разделение случайности и сущности. То есть опознавание тех характерных черт, которые возникают из быстротечных технических обстоятельств, и тех особенностей, которые сильно связаны с центральной сложностью Unix-проектирования — как правильно использовать модульность и абстракцию, одновременно сохраняя системы прозрачными и простыми.

    Подобное различие может быть трудным, поскольку характерные особенности, которые возникли случайно, иногда обнаруживают существенную пользу. В качестве примера рассмотрим правило "молчание — золото" в проектировании Unix-интерфейса, которое рассматривалось в главе 11; оно родилось как способ адаптации к медленным телетайпам, однако сохранилось впоследствии, поскольку программы с лаконичным выводом можно было проще комбинировать в сценариях. В настоящее время в среде, где обычной практикой является запуск нескольких программ в визуальном режиме посредством GUI-интерфейса, проявляется еще одно полезное свойство: немногословные программы не отвлекают понапрасну внимание пользователя.

    Напротив, некоторые характерные черты, которые однажды казались существенными для Unix, оказались случайностями, связанными с определенным набором соотношений издержек. Например, предпочтительные в старой школе Unix программные конструкции (и мини-языки, такие как awk(1)), осуществлявшие построчную обработку входного потока или обрабатывавшие двоичные файлы последовательно от записи к записи, в любом контексте, который необходимо было поддерживать между фрагментами сложного кода конечных автоматов. С другой стороны, Unix-проектирование новой школы, как правило, использует предположение о том, что программа может считывать весь ввод в память, а впоследствии при необходимости использовать произвольный доступ к этим данным. Действительно, современные Unix-системы предоставляют вызов mmap(2), который позволяет программисту отображать весь файл на виртуальную память и полностью скрывать сериализацию ввода-вывода с дисковым накопителем.

    Это изменение позволяет отказаться от экономии дискового пространства, а взамен получить более простой и прозрачный код, т.е. изменяется соотношение понижающейся стоимости памяти и стоимости времени программиста. Множество отличий между конструкциями старой школы Unix в 70-х и 80-х годах прошлого века и конструкциями новой школы после 1990 года можно связать с огромным сдвигом в относительной стоимости. В результате этого сдвига в настоящее время все аппаратные ресурсы становятся на несколько порядков дешевле по отношению к времени программиста, чем это было в 1969 году.

    Оглядываясь назад, можно установить три специфических технологических изменения, которые повлекли значительные перемены в стиле Unix-проектирования: межсетевой обмен, растровые графические дисплеи и персональные компьютеры. В каждом случае традиция Unix приспосабливалась к трудностям, отказываясь от тех случайностей, к которым более невозможно было адаптироваться без новых способов применения своих основных идей. Биологическая эволюция движется в том же направлении. У эволюционных биологов есть правило: "Не предполагать, что историческое происхождение определяет современную пользу или наоборот". Кратко рассматривая то, как Unix приспособилась в каждом из описанных случаев, можно заметить некоторые подсказки относительно того, как Unix может адаптироваться к будущим, еще непредвиденным технологическим переменам.

    В главе 2 описана первая из этих перемен: возникновение межсетевого обмена с точки зрения истории культуры. В главе 2 рассказывается, как протокол TCP/IP связал в одно целое исходную культуру Unix и культуру сети ARPANET после 1980 года. В главе 7 материал, описывающий устаревшие методы IPC и сетевые методы, такие как System V STREAMS, указывает на то, как много было заблуждений, оплошностей и тупиковых ветвей, которые захватывали Unix-разработчиков большую часть последующего десятилетия. Было много путаницы, связанной с протоколами[148], сетевым взаимодействием машин и взаимным обменом данными между процессами на одной машине.

    В конце концов, путаница исчезла, когда протокол TCP/IP победил, и BSD-сокеты заново утвердили важнейшую метафору Unix: "Все является потоком байтов". Стало нормой использовать BSD-сокеты как для IPC, так и для сетевого взаимодействия. Более ранние методы в обеих областях почти совершенно вышли из употребления, и программное обеспечение Unix развивалось все более индифферентно относительно того, расположены ли обменивающиеся данными компоненты на одной машине или на разных машинах. Логическим результатом этого было создание World Wide Web в 1990–1991 годах.

    Когда в 1984 году через несколько лет после TCP IP появилась растровая графика и пример Macintosh, возникла еще более сложная проблема. Исходные GUI-интерфейсы от Xerox PARK и Apple были замечательными, однако они связывали вместе слишком много уровней системы для того, чтобы Unix программисты чувствовали себя комфортно с такой конструкцией. Немедленным ответом Unix-программистов стал явный принцип отделения политики от механизма. Система X Window была представлена к 1988 году. Они отделили наборы полезных компонентов X от диспетчера дисплея, который выполнял низкоуровневую обработку графики. Благодаря этому была создана архитектура, которая была модульной и четкой в понятиях Unix, а также позволяла с течением времени проще развивать улучшения в политике.

    Однако это была простая часть проблемы. Сложной ее частью было решение — должна ли вообще Unix иметь унифицированную политику интерфейса, и если да, то какой она должна быть. Несколько различных попыток представить ее посредством частных инструментальных наборов (таких как Motif) провалились. В настоящее время в этой области конкурируют инструментарии GTK и Qt. Несмотря на то, что дебаты по этому вопросу не прекратились, постоянство различных стилей пользовательского интерфейса, рассмотренных в главе 11, впечатляет. Unix-проектирование новой школы сохранило командную строку и справилось с напряжением между подходами GUI и CLI благодаря связыванию большого количества пар CLI-ядро/GUI-интерфейс, которые могут использоваться в обоих стилях.

    Как технология, персональный компьютер представил несколько главных проблем проектирования. Процессоры 386-й серии и более поздние версии были достаточно мощными для того, чтобы предоставить системам, разработанным на их основе, соотношение затрат, подобное соотношению, характерному для мини-компьютеров, рабочих станций и серверов, на которых сформировалась операционная система Unix. Истинной трудностью было изменение потенциального рынка для Unix- систем; более низкая общая стоимость аппаратного обеспечения сделала персональные компьютеры привлекательными для чрезвычайно широкой и менее технически искушенной категории пользователей.

    Поставщики частных Unix-систем, привыкшие к большей прибыли от продажи более мощных систем опытным покупателям, никогда не интересовались этим рынком. Первые серьезные инициативы по направлению к настольным системам конечных пользователей исходили от сообщества открытого исходного кода и были восприняты в основном по идеологическим причинам. Согласно аналитическим исследованиям рынка, по состоянию на середину 2003 года операционная система Linux заняла около 4-5% этого рынка, что вполне сопоставимо с объемами Apple Macintosh.

    Независимо от того покажет ли Linux когда-либо более высокие результаты, ответ Unix-сообщества уже ясен. Об этом уже говорилось в главе 3, при рассмотрении вопроса о заимствовании нескольких технологий (таких как XML) из других систем и натурализации GUI-интерфейсов в Unix-мире. Однако основное внимание все-таки уделяется модульности и четкому коду — созданию инфраструктуры для серьезных высоконадежных вычислений и коммуникаций.

    Этот акцент подтверждается историей крупномасштабных проектов, подобных Mozilla и OpenOffice.org, которые стартовали в конце 90-х годов. В обоих указанных случаях наиболее важной темой в отклике сообщества было отнюдь не требование новых функций или соблюдение сроков поставки. Главной идеей в сообществе была неприязнь к гигантским монолитам и общее понимание того, что прежде чем эти большие программы перестанут быть препятствием, их придется облегчить, подвергнуть рефакторингу и разделить на модули.

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

    20.2. Plan 9: каким представлялось будущее Unix

    Известно, как обычно представляется будущее Unix. Оно было определено исследовательской группой Bell Labs, которая построила Unix, в работе под названием "Plan 9 from Bell Labs"[149]. Операционная система Plan 9 представляла собой попытку воссоздать Unix и сделать ее лучше.

    Главной проблемой проектирования, которую конструкторы попытались разрешить в операционной системе Plan 9, была интеграция графики и повсеместного использования сети в комфортабельной Unix-подобной структуре. Они придерживались выбора Unix, организовывая промежуточный доступ к любому возможному количеству системных служб посредством единого, большого иерархического пространства имен файлов. Фактически они его улучшили. Многие средства, которые в Unix были доступны посредством различных узкоспециальных интерфейсов, подобных BSD-сокетам, fcntl(2) и ioctl(2), в операционной системе Plan 9 были доступны посредством обычных операций чтения и записи в специальные файлы аналогичные файлам устройств. Для обеспечения переносимости и простого доступа почти все интерфейсы устройств были текстовыми, а не двоичными. Большинство системных служб (включая, например, систему оконного интерфейса) представляли собой файловые серверы, содержащие специальные файлы или деревья каталогов, представляющие обслуживаемые ресурсы. Представляя все ресурсы в виде файлов, операционная система Plan 9 превратила проблему доступа к ресурсам, расположенным на различных серверах, в проблему доступа к файлам на различных серверах.

    В операционной системе Plan 9 файловая модель, еще больше соответствующая духу Unix, чем модель самой Unix, была объединена с новой идеей: частным пространством имен. Каждый пользователь (а, по сути, каждый процесс) могли иметь собственное представление системных служб путем создания собственного дерева точек монтирования файловых серверов. Некоторые точки монтирования файловых серверов устанавливаются вручную пользователем, а другие автоматически устанавливаются во время регистрации пользователя в системе. Поэтому (как указано в обзорной статье "Plan 9 from Bell Labs") "

    /dev/cons
    всегда ссылается на терминальное устройство, a
    /bin/date
    на корректную версию команды date, однако определение файлов, которые должны быть представлены этими именами, зависит от различных обстоятельств, таких как архитектура машины, выполняющей команду
    date
    ".

    Наиболее важная особенность операционной системы Plan 9 заключается в том, что все подключенные файловые серверы предоставляют одинаковый интерфейс, подобный файловой системе, независимо от скрытой за ними реализации. Некоторые из них могут соответствовать локальным файловым системам, некоторые — удаленным файловым системам, доступ к которым происходит по сети, некоторые могут соответствовать экземплярам системных серверов, запущенных в пользовательском пространстве (например, система оконного интерфейса или альтернативный набор сетевых протоколов), а некоторые могут соответствовать интерфейсам ядра. Для пользователей и клиентских программ все описанные случаи выглядят одинаково.

    В обзорной статье о Plan 9 представлен способ, с помощью которого реализован FTP-доступ к удаленным узлам. В операционной системе Plan 9 не существует команды ftp(1). Вместо нее используется файловый сервер ftpfs, а каждое FTP-соединение выглядит как точка монтирования файловой системы. Сервер ftpfs автоматически преобразовывает команды открытия, чтения и записи файлов и каталогов в точке монтирования в FTP-транзакции. Таким образом, все обычные инструменты обработки файлов, такие как ls(1), mv(1) и ср(1), просто работают как в точке монтирования FTP, так и через границы с остальной частью пользовательского представления пространства имен. Единственное отличие состоит в том, что пользователь (или его сценарии и программа) замечают разницу в скорости получения данных.

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

    Можно назвать множество специфических причин — недостаток каких-либо серьезных попыток продвижения данной системы на рынке, ограниченная документация, большая путаница и препятствия, связанные с платой и лицензионными отчислениями. Тем, кто незнаком с Plan 9, кажется, что она функционирует в основном как опытный образец для написания интересных статей по исследованию операционных систем. Однако сама Unix ранее преодолела все подобные препятствия и привлекла преданных последователей, распространивших ее по всему миру. Почему же этого не случилось с Plan 9?

    Глубокий анализ данной истории мог бы, конечно, прояснить ситуацию, но в 2003 году она такова, что Plan 9 провалилась просто потому, что не стала настолько убедительным усовершенствованием Unix, чтобы заменить свою предшественницу. По сравнению с Plan 9, Unix "скрепит, гремит и имеет очевидные пятна ржавчины", однако, она выполняет свою работу достаточно хорошо для того, чтобы удерживать позиции. Это урок для честолюбивых системных архитекторов: самым опасным врагом наилучшего решения является существующая база кода, которая просто достаточно хороша.

    Некоторые идеи Plan 9 были впитаны современными Unix-системами, особенно наиболее инновационными версиями с открытым исходным кодом. А во FreeBSD файловая система

    /proc
    смоделирована в точности, как в Plan 9, и ее можно использовать для опроса или управления работающими процессами. Системные вызовы rfork(2) в FreeBSD и clone(2) в Linux смоделированы на основе rfork(2) в Plan 9. Файловая система
    /proc
    в Linux, кроме информации о процессах, содержит еще и множество файлов устройств, синтезированных наподобие Plan 9, которые используются для опроса и управления внутренними параметрами ядра с помощью преимущественно текстовых интерфейсов. Экспериментальные версии Linux 2003 года реализовали точки монтирования процессов, что является серьезным шагом в сторону частных пространств имен Plan 9. Различные Unix-системы с открытым исходным кодом двигаются в направлении общесистемной поддержки кодировки UTF-8, которая фактически была создана для Plan 9[150].

    Вполне вероятно, что со временем гораздо больше функций Plan 9 будут работать в Unix, по мере того как различные части архитектуры Unix будут плавно устаревать. Это одно из возможных направлений развития будущей Unix.

    20.3. Проблемы в конструкции Unix

    Операционная система Plan 9 "очищает" Unix, но добавляет лишь одну новую концепцию (частное пространство имен) к ее основному набору конструктивных идей. Однако есть ли серьезные проблемы в этих базовых идеях? В главе 1 рассматривалось несколько областей, в которых Unix, вероятно, можно считать неудачной. В настоящее время, когда движение в поддержку открытого исходного кода "передало будущее" конструкции Unix обратно в руки программистов и технических специалистов, эти проблемы близки к разрешению. Ниже проблемы Unix рассматриваются снова, для того чтобы лучше понять, как Unix будет развиваться в будущем.

    20.3.1. Unix-файл представляет собой только большой блок байтов

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

    В более широком смысле все рассматривается как поток байтов. Даже аппаратные устройства являются потоками байтов. Данная метафора была большим успехом ранней Unix и большим преимуществом в мире, где (например) компилируемые программы не могли осуществлять вывод, который мог бы быть отправлен обратно компилятору. Из данной метафоры выросли каналы и shell-программирование.

    Однако метафора байтового потока в Unix настолько существенна, что в Unix имеются проблемы при интеграции программных объектов с операциями, которые не вписываются в байтовый поток или файловый состав операций (создание, открытие, чтение, запись, удаление). Это особенно является проблемой для GUI-объектов, таких как пиктограммы, окна и "активные" документы. Внутри классической Unix- модели мира единственный способ расширить существующую метафору байтового потока заключается в использовании вызовов

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

    Приверженцы операционных систем семейства Macintosh склонны громогласно критиковать это. Они пропагандируют модель, в которой одно имя файла может иметь как "ветвь" данных, так и "ветвь" ресурсов. Ветвь данных соответствует байтовому потоку в Unix, а "ветвь" ресурсов является семейством пар имя/значение. Приверженцы Unix предпочитают такие подходы, которые делают данные файла самоописательными, так чтобы фактически те же метаданные хранились внутри файла.

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

    С другой стороны, поддержка файловых атрибутов сопряжена с трудными вопросами о том, какие файловые операции должны их сохранять. Очевидно, что при копировании именованного файла в другой именованный файл должны также копироваться атрибуты файла источника, как и его данные. Однако что произойдет, если файл был перенаправлен в новый файл с помощью команды cat(1)?

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

    Разработчики файловой системы Xerox PARC боролись с данной проблемой в 70-х годах прошлого века. Для конструкции был характерен "открытый сериализованный" вызов, который возвращал байтовый поток, содержащий как атрибуты, так и содержимое файла. Если вызов применялся к каталогу, то он возвращал сериализацию атрибутов каталога плюс сериализацию всех файлов, хранящихся в нем. Не заметно, чтобы этот подход когда-либо был усовершенствован.

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

    20.3.2. Слабая поддержка GUI-интерфейсов в Unix

    Опыт Unix доказывает, что использование нескольких метафор в качестве базиса для интегрирующей структуры представляет собой мощную стратегию (обсуждение интегрирующих структур и общего контекста приведено в главе 13). Визуальная метафора "в сердце" современных GUI-интерфейсов (файлы, представленные пиктограммами и открываемые щелчком мыши, который запускает некоторую программу, предназначенную для обработки и обычно способную создавать и редактировать данные файлы) с момента ее разработки в Xerox PARC в 1970-х годах доказала свою способность успешно и долговременно удерживать пользователей и разработчиков интерфейсов.

    Несмотря на значительные недавние усилия, в 2003 году Unix все еще поддерживала эту метафору слабо и неохотно — существовало множество уровней, несколько соглашений и только слабые конструкционные утилиты. Типичная реакция со стороны опытных профессионалов Unix — подозревать, что это отражает более глубокие проблемы с самой GUI-метафорой.

    Я думаю, что часть проблемы заключается в том, что мы до сих пор не имеем правильной метафоры. Например, в Macintosh, для того чтобы удалить файл, я перетаскиваю его в корзину, но когда я перетаскиваю его на диск, то файл копируется, кроме того, я не могу перетащить файл на пиктограмму принтера, для того чтобы распечатать его, потому что для этого используется меню. Я мог бы продолжать. Это похоже на файлы в OS/360, до того как появилась Unix с ее простой (но не слишком простой) файловой идеей.

    (Стив Джонсон.)

    В главе 11 приводились цитаты Брайана Кернигана и Майка Леска по этому же поводу, но проблему невозможно решить, лишь обвиняя GUI-интерфейс. Несмотря на все его недостатки, имеется огромная потребность в GUI-интерфейсах со стороны конечных пользователей. Предположим, что удалось создать правильную метафору на уровне взаимодействия с пользователем. Была бы в таком случае Unix способна изящно поддерживать GUI-интерфейс?

    Вероятно, нет. Авторы касались данной проблемы, анализируя, является ли модель представления файлов в виде большого блока байтов адекватной. Для более развитой поддержки GUI-интерфейсов механизм мог бы быть предоставлен файловыми атрибутами в стиле Macintosh. Однако кажется маловероятным, что такой подход полностью разрешит проблему. Объектная модель в Unix не включает в себя верных фундаментальных конструкций. Необходимо определить, какой в действительности была бы строгая интегрирующая структура для GUI интерфейсов. И, что не менее важно, как ее можно интегрировать с существующими структурами Unix. Это сложная проблема, требующая фундаментального понимания, которое пока невозможно "среди шума и путаницы" программной инженерии и академических исследований.

    20.3.3. Удаление файлов в Unix необратимо

    Пользователи с опытом работы в системе VMS или те, кто помнит TOPS-20, часто испытывают недостаток средств для контроля версий файлов, которые были характерны для этих систем. Открытие существующего файла для записи или удаления фактически приводило к его переименованию предсказуемым способом, а новое имя включало в себя номер версии. И только явная операция удаления файла версии фактически стирала данные.

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

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

    20.3.4. Unix предполагает статичную файловую систему

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

    В операционной системе Linux предусмотрена функция уведомления об изменениях файлов и каталогов[151], и эти функции скопированы в некоторых версиях BSD, однако они еще не перенесены на другие Unix системы.

    20.3.5. Конструкция системы управления задачами была плохо реализована

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

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

    При правильной реализации потребовалось бы, чтобы tty-драйвер Unix не просто поддерживал буфер линии, но и полностью отслеживал текущее состояние экрана. Кроме того, потребовалось бы, чтобы сведения о типах терминалов были известны на уровне ядра (возможно, с помощью процесса демона), для того чтобы оно могло соответствующим образом выполнить восстановление, когда приостановленный процесс снова переводится в приоритетный режим. Последствия неправильной реализации заключаются в том, что ядро не способно отключить сеанс как задачу xterm или Emacs от одного терминала и подключить его к другому терминалу (тип которого мог бы отличаться).

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

    Широко распространенная программа с открытым исходным кодом, которая называется screen(1), решает некоторые из этих проблем[153]. Однако поскольку пользователь должен ее вызвать явно, не гарантируется, что ее возможности будут присутствовать в каждом терминальном сеансе. Кроме того, код уровня ядра, который перекрывает ее в функциональной части, не был удален.

    20.3.6. В Unix API не используются исключительные ситуации

    Язык С испытывает недостаток средств восстановления для обработки именованных исключительных ситуаций со связанными данными[154]. Таким образом, C-функции в Unix API сообщают об ошибках путем возвращения известного значения (обычно -1 или указатель на NULL-символ) и установки глобальной переменной errno.

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

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

    Данная проблема может быть скрыта (и обычно скрывается) привязками Unix API в таких языках, как Python или Java, в которых есть исключительные ситуации.

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

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

    20.3.7. Вызовы ioctl(2) и fcntl(2) являются препятствиями

    Механизмы ioctl(2) и fcntl(2) обеспечивают способ написания перехватчиков (hooks) в драйверах устройств. Первоначальным историческим использованием ioctl(2) была установка параметров, таких как скорость передачи и количество фреймирующих битов в драйверах последовательных линий, отсюда и название ("I/O control"). Позднее вызовы ioctl были добавлены для других драйверов, a fcntl(2) был добавлен как перехватчик в файловую систему.

    С годами вызовы

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

    Основная проблема в данном случае та же, что и "большой блок байтов"; объектная модель Unix слабая и не оставляет естественного пространства для размещения многих вспомогательных операций. Разработчики получают сложный выбор из неудовлетворительных альтернатив. Вызовы

    fcntl
    /
    ioctl
    проходят через устройства в /dev, новые специализированные системные вызовы или методы через специализированные виртуальные файловые системы, которые привязаны к ядру (например, /proc Linux и др.).

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

    ioctl
    /
    fcntl
    (это, по крайней мере, исключило бы необходимость в применении макроопределений перед использованием интерфейса). Выше уже отмечалось, что операционная система Plan 9, в которой вместо модели файл/поток байтов используется именованный файловый сервер или файловая система как базовый объект, представляет другой возможный путь.

    20.3.8. Модель безопасности Unix, возможно, слишком примитивна

    Возможно, полномочия пользователя root слишком широки, и в Unix должны быть возможности более четкой градации полномочий или ACL (Access Control Lists — списки контроля доступа) для функций системного администрирования, чем один суперпользователь, который может все. Люди, которые принимают данную позицию, спорят, что многие системные программы имеют постоянные root- привилегии благодаря механизму setuid. Если даже одну из них можно будет взломать, то вторжения последуют везде.

    Однако это довольно слабый аргумент. Современные Unix-системы позволяют включать учетную запись любого пользователя в несколько групп. Используя полномочия на выполнение, а также установку битов идентификатора группы на выполняемые файлы программ, можно заставить каждую группу функционировать в качестве ACL-списка для файлов или программ.

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

    20.3.9. Unix имеет слишком много различных видов имен

    Unix объединила файлы и локальные устройства — они являются просто потоками байтов. Однако сетевые устройства, доступные через сокеты, имеют иную семантику и другое пространство имен. Plan 9 показывает, что файлы вполне можно сочетать как с локальными, так и с удаленными (сетевыми) устройствами, и все это может управляться через пространство имен, которое способно динамически настраиваться для каждого пользователя и даже для каждой программы.

    20.3.10. Файловые системы могут считаться вредными

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

    Современные исследования в данном направлении (такие как EROS[155]) подтверждают, что такие конструкции могут предоставлять большие преимущества, включая доказуемое соответствие политике безопасности и более высокую производительность. Однако следует отметить, что если это проигрыш Unix, то это равный проигрыш всех ее конкурентов. Ни одна крупная действующая операционная система еще не предпочла направление EROS[156].

    20.3.11. На пути к глобальному адресному пространству Internet

    Возможно, URL-адреса недостаточно успешны. Оставим последнее слово о возможных направлениях будущего развития Unix за создателем этой системы.

    Моим идеалом будущего является развитие удаленного интерфейса файловой системы (а ля Plan 9), а затем его реализация в Internet как стандарта вместо HTML. Это было бы действительно здорово.

    (Кен Томпсон.)

    20.4. Проблемы в окружении Unix

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

    Одна из таких проблем — как сделать разработку открытого исходного кода экономически целесообразной? Мы вновь "вернулись к нашим корням" в общем, открытом процессе ранней эпохи Unix. Мы почти совершенно выиграли технический спор об отказе от секретности и частного управления. Мы продумали способы сотрудничества с рынками и менеджерами на более равноправных условиях, чем это было возможно в 70-х и 80-х годах прошлого века, и во многом наши эксперименты были успешными. В 2003 году Unix-системы с открытым исходным кодом и их основные группы разработчиков достигли такого авторитета в обществе, который не так давно, еще в середине 90-х годов был бы невообразимым.

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

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

    В целом, можно ожидать, что денежные затраты на найм разработчиков программного обеспечения будут расти по тем же причинам, по которым и почасовая оплата механиков растет по мере снижения цен на автомобили[157]. Однако контролировать такие траты для любого индивидуума или фирмы будет все труднее. Будет много состоятельных программистов, но меньше миллионеров. Фактически это признак прогресса, неспособности вырваться из системы. Но это повлечет крупные изменения на рынке, а возможно, это будет означать, что инвесторы потеряют тот небольшой интерес, который они проявляли при финансировании новых программных проектов.

    Одной важной подзадачей, связанной с возрастающей трудностью поддержки действительно крупных программных предприятий, является организация тестирования силами конечных пользователей. Исторически концентрация Unix-культуры на инфраструктуре означала, что мы не стремились создавать программы, ценность которых зависела от предоставления комфортного интерфейса для конечных пользователей. В настоящее время, особенно в Unix-системах с открытым исходным кодом, которые стремятся конкурировать непосредственно с Microsoft и Apple, ситуация изменяется. Однако пользовательские интерфейсы должны систематически тестироваться реальными конечными пользователями — и в этом отношении наблюдаются некоторые трудности.

    Тестирование со стороны реальных конечных пользователей требует средств, специалистов и уровня мониторинга, которые трудно достижимы для распределенных групп добровольцев, характерных для разработки открытого исходного кода. Следовательно, может оказаться, что текстовые процессоры, электронные таблицы и другие "продуктивные" приложения придется оставить в руках крупных, поддерживаемых корпорациями проектов, таких как OpenOffice.org, которые могут позволить себе такие издержки. Разработчики открытого исходного кода считают единые корпорации основной причиной затруднений и беспокоятся о такой зависимости, однако лучшее решение пока не найдено.

    Все эти проблемы экономические. Существуют и другие проблемы, более "политические", поскольку успех создает врагов.

    Некоторые из них известны. Стремление Microsoft к непотопляемой монополии на компьютерные вычисления в середине 1980-х годов сделало поражение Unix стратегической целью компании (т.е. еще за пять лет до того, как мы узнали, что втянуты в борьбу). В середине 2003 года, несмотря на то, что несколько развивающихся рынков, на которые рассчитывала корпорация, были почти полностью захвачены системой Linux, Microsoft являлась богатейшей и наиболее мощной программной компанией в мире. Microsoft хорошо известно — для того чтобы выжить, ей необходимо победить новые Unix-системы, созданные внутри движения открытого исходного кода, а для того чтобы сделать это, она должна разрушить или дискредитировать создавшую их культуру.

    Возвращение Unix в сообщество открытого исходного кода и ее связь со свободной культурой Internet также создали новых врагов. Голливуд и крупные медиа- корпорации чувствуют угрозу со стороны Internet и инициировали многовекторную атаку на неконтролируемую разработку программного обеспечения. Существующее законодательство, такое как Digital Millennium Copyright Act (Закон об охране авторских прав в цифровом тысячелетии), уже не раз использовалось для судебного преследования разработчиков, которые создавали программы, неугодные медиа-магнатам (самые известные случаи, несомненно, связаны с программным обеспечением DeCSS, которое позволяет проигрывать зашифрованные DVD-диски). Уже продуманы схемы, подобные так называемому "Trusted Computing Platform Alliance" (альянс достоверных вычислительных платформ) и "Palladium threaten"[158], чтобы сделать разработку открытого исходного кода фактически незаконной. И если открытый исходный код придет в упадок, то Unix, весьма вероятно, придет в упадок вместе с ним.

    Unix, хакеры и Internet против Microsoft, Голливуда и медиа-гигантов. Это борьба, которую необходимо выиграть по всем традиционным причинам профессионализма, преданности ремеслу и взаимной преданности сообществу. Однако существуют и более весомые причины этой борьбы. Возможности политики все более формируются коммуникационными технологиями — сильнее сегодня тот, кто может использовать их, кто может подвергать их цензуре, кто может их контролировать. Правительственный и корпоративный контроль над содержимым сетей, а также тем, что люди могут делать с помощью своих компьютеров, является ощутимой долгосрочной угрозой политической свободе. Кошмарным сценарием представляется ситуация, когда корпоративный монополизм и политики, непрерывно ищущие власти, как постоянные естественные союзники, объединятся друг с другом и создадут подоплеку для возрастающего регулирования, репрессий и криминализации цифрового общения. Противостоим этому мы — воины свободы — свободы всеобщей, а не только своей собственной.

    20.5. Проблемы в культуре Unix

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

    Меньшая трудность — трение между гуру старой Unix-школы и приверженцами открытого исходного кода новой школы. Успех Linux, в частности, не является полностью удобным феноменом для многих старых Unix-программистов. Частично это проблема поколения. Сильная энергия, наивный и веселый фанатизм поколения Linux иногда раздражает старейшин, переживших 70-е годы и (часто справедливо) считающих себя мудрее. Тот факт, что дети добиваются успеха там, где отцы терпят неудачу, только обостряет данную проблему.

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

    Все Macintosh-программисты "вращаются" вокруг пользовательского опыта. Они — архитекторы и декораторы. Они проектируют снаружи внутрь, в первую очередь задаваясь вопросом: "Какой вид взаимодействия необходимо поддерживать?", а затем на заднем плане строят прикладную логику для удовлетворения запросов конструкции пользовательского интерфейса. Это приводит к появлению очень красивых программ и слабой, неустойчивой инфраструктуры. В одном печально известном примере MacOS 9 диспетчер памяти иногда требовал от пользователя высвобождения памяти вручную путем остановки программы (также вручную), которая уже завершила работу, но все еще остается резидентной. Приверженцы Unix внутренне протестуют против такой ошибочной конструкции; они не понимают, как пользователи Macintosh могут с этим мириться.

    Напротив, Unix-программисты полностью заняты инфраструктурой. Мы — водопроводчики и каменотесы. Мы проектируем изнутри, создавая могучие машины для решения абстрактно определенных проблем (например: "Как обеспечить надежную доставку пакетов из пункта А в пункт В через ненадежную аппаратуру и каналы?"). Затем мы скрываем машины за тонкими и часто крайне уродливыми интерфейсами. Команды date(1), find(1) и ed(1) являются широко известными примерами, но существуют еще сотни других. Приверженцы Macintosh внутренне протестуют против такой ошибочной конструкции; они не понимают, как пользователи Unix могут с этим мириться.

    Обе конструкторские философии обоснованы, но два лагеря имеют большие сложности при рассмотрении противоположных точек зрения. Типичный рефлекс Unix-разработчика — выбрасывать из головы Macintosh-программы как кричащую массу, приятные для глаза дилетанта картинки, и продолжать создавать программное обеспечение, которое притягивает внимание других Unix-разработчиков. И если пользователям программа не нравится — тем хуже для пользователей; они вернутся, когда им откроется тайна.

    Во многом такая ограниченность хорошо нам послужила. Мы — хранители Internet и World Wide Web. Наше программное обеспечение и наши традиции господствуют в серьезных вычислениях, приложениях, где обязательными требованиями являются надежность двадцать четыре часа в сутки и семь дней в неделю, а также минимальные простои. Мы действительно чрезвычайно хорошо создаем прочную инфраструктуру; мы ни в коем случае не идеальны, но не существует другой культуры создания программного обеспечения, которая приблизилась бы к нашим рекордам, и мы гордимся своей культурой.

    Проблема в том, что мы все больше сталкиваемся с трудностями, которые требуют более широкого взгляда. Большинство компьютеров в мире находятся не в серверных комнатах, а скорее в руках тех самых конечных пользователей. В ранние дни Unix, до персональных компьютеров, наша культура определяла себя частично как протест против культа мэйнфреймов, хранителей "большого железа". Позднее мы впитали идеализм ранних энтузиастов мини-компьютеров: мощные компьютеры — людям. Но сегодня мы являемся служителями культа; мы люди, которые поддерживают сети и "большое железо". И наше скрытое требование гласит: "Если вы хотите использовать наши программы, то вы должны научиться думать, как мы".

    В 2003 году в нашей идеологии наметилось глубокое противоречие — внутренний конфликт между высокомерием и миссионерским популизмом. Мы хотим переубедить и переделать 92% пользователей, для которых компьютеры означают игры, мультимедиа, блестящие GUI-интерфейсы и (что касается наиболее технических пользователей) легкие почтовые программы, текстовые процессоры и электронные таблицы. Мы затрачиваем значительные усилия на проекты, подобные GNOME и KDE, предназначенные для того, чтобы придать Unix симпатичное лицо. Но в сердце мы до сих пор высокомерны, глубоко не желаем, а во многих случаях не способны идентифицировать или выслушивать пожелания неискушенных пользователей.

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

    Величайшая наша проблема как культуры — сможем ли мы перерасти предположения, которые так хорошо нам служили, сможем ли мы признать не только интеллектуально, но и силой повседневной практики, что Macintosh-разработчиков можно понять? Их точка зрения в более широком смысле, но менее характерно для Mac описана в "The Inmates Are Running the Asylum" [14], проницательной и логичной книге о том, что ее автор называет дизайном взаимодействия, содержащей (несмотря на случайные причуды) много тяжелой правды, которую следовало бы знать каждому Unix-программисту.

    Мы можем уклониться от этого; мы можем остаться служителями культа, привлекая избранное меньшинство лучших и талантливейших, образованную элиту, сфокусированную на нашей исторической роли хранителей программной инфраструктуры и сетей. Но в таком случае наша культура, весьма вероятно, придет в упадок и со временем потеряет динамизм, который поддерживал нас в течение десятилетий. Кто-то другой будет служить людям; кто-то другой придет туда, где будут мощности и деньги, и будет править будущим 92% всего программного обеспечения. Есть вероятность, независимо от того, будет ли этим кто-то именно Microsoft, что они будут делать это с помощью практик и программ, которые нам не очень нравятся.

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

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

    Примечательно, что культура Macintosh начала объединяться с нашей — в основе MacOS X стоит Unix, и сегодня Mac-разработчики (хотя и в некоторых случаях не без трудностей) приспосабливают свой разум к изучению достоинств Unix, сфокусированных на инфраструктуре. Аналогично, нашей проблемой будет принять достоинства Macintosh, связанные с ориентацией системы на пользователя.

    Имеются также другие признаки того, что Unix-культура избавляется от своей изолированности. Одним из них является слияние, которое, по всей видимости, будет продолжаться, сообщества Unix/открытый исходный код с движением, которое называется "гибкое программирование" (agile programming)[159]. В главе 4 отмечалось, что Unix-программисты удачно ухватились за идею рефакторинга, одного из предубеждений мыслителей гибкого программирования. Рефакторинг и другие концепции гибкого программирования, такие как блочное тестирование (unit-testing) и дизайн вокруг областей (design around stories), кажется, ясно выражают и обостряют практические приемы, которые до этого были широко распространены в Unix-традиции, но только неявно. С другой стороны, Unix-традиция может привнести в гибкое программирование устойчивость и уроки многолетнего опыта. Поскольку открытое программное обеспечение приобретает свою долю рынка, очень вероятно, что эти культуры объединятся, как это было с ранними культурами Internet и Unix после 1980 года.

    20.6. Причины верить

    В будущем операционной системы Unix много проблем. Хотели бы мы действительно изменить его?

    За более чем тридцатилетнюю историю мы преуспели в разрешении многих трудностей. Мы были первопроходцами лучших практических приемов программной инженерии. Мы создали сегодняшние Internet и Web. Мы создали крупнейшие, наиболее сложные и самые надежные программные системы из когда-либо существовавших. Мы пережили монополию IBM и выступили против монополии Microsoft.

    Однако не все это можно назвать триумфом. В 1980-х годах мы почти разрушили свою культуру, согласившись с частным захватом Unix. Мы долго пренебрегали компьютерами низкого класса и нетехническими конечными пользователями и таким образом предоставили Microsoft удобный случай для внедрения низкокачественных стандартов программного обеспечения. Умные наблюдатели неоднократно объявляли крах нашей технологии, сообщества и наших ценностей.

    Но мы всегда стремительно возвращались. Мы делаем ошибки, но мы учимся на своих ошибках. Мы передали нашу культуру через поколения; мы впитали многое из лучшего опыта ранних академических хакеров, ARPANET-экспериментаторов, энтузиастов мини-компьютеров и множества других культур. Движение открытого исходного кода возродило силу и идеализм наших ранних лет, и сегодня мы сильнее и многочисленнее, чем когда-либо.

    До сих пор доводы против Unix-хакеров всегда были в краткосрочном выигрыше, но проигрывали в долгосрочной перспективе. Мы можем победить, если решимся на это.


    Примечания:



    1

    Три с половиной десятилетия между 1969 и 2003 гг. — это время исторической эволюции ОС Unix, воплотившей достижения более 50 млн. человеко-лет.



    13

    Существует Web-версия списка часто задаваемых вопросов (FAQ) по PDP-компьютерам <http://www.faqs.org/faqs/dec-faq/pdp8>, в котором по-другому трактуется роль компьютера PDP-7 в истории.



    14

    Руководства по седьмой версии доступны на странице http://plan9.bell-labs.com/7thEdMan/index.html.



    15

    UUCP считалась великолепным средством, когда быстрый модем обеспечивал скорость 300 бод.



    136

    Обе системы PDP-7 Unix и Linux стали примерами "неожиданной живучести". Unix в качестве "игрушки для опытов" была создана несколькими исследователями в перерывах между основными проектами. Наполовину она была предназначена для экспериментов с файловой системой и наполовину для поддержки компьютерной игры. Linux была охарактеризована своим создателем как "мой эмулятор терминала с выросшими ногами" [85]



    137

    Таким образом, литера "С" в названии языка С означает Common (общий) или, возможно, "Christopher". Аббревиатура BCPL первоначально расшифровывалась как "Bootstrap CPL" — сильно упрощенная версия языка CPL, весьма интересного, но слишком претенциозного языка общего программирования (Common Programming Language) Оксфордского и Кембриджского университетов, также называемого "языком программирования Кристофера" (Christopher Programming Language) по имени его главного пропагандиста, первопроходца компьютерной науки Кристофера Стрэчи (Christopher Strachey).



    138

    Документ доступен в Web: <http://anubis.dkuug.dk/JTC1/SC22/WG14/www/charter>.



    139

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



    140

    Один Linux-дистрибьютор, а именно Лазермун (Lasermoon) из Великобритании, добился сертификации POSIX.1 FIPS 151-2, но вышел из бизнеса, поскольку потенциальных клиентов сертификация не интересовала.



    141

    Эта тема обсуждается в книге "Just for Fun " [85]



    142

    Web-поиск, вероятно, предоставит популярную страницу, на которой сатирически описывается семиуровневая модель OSI сравнивается.



    143

    Эти слова впервые были произнесены членом руководства IETF Дэйвом Кларком (Dave Clark) в 1992 году на бурном собрании, в ходе которого IETF отвергла протокол открытого взаимодействия систем.



    144

    9 RFC 1149 доступен в Web — <http://www.ietf.org/rfc/rfc1149.txt>. Его реализация описана на стр. <http://www.blug.linux.no/rfc1149/writeup.html>.



    145

    10 RFC 2324 доступен в Web — <http://www.ietf.org/rfc/rfc2324.txt>.



    146

    11 RFC 3514 доступен в Web — <http://www.ietf.org/rfc/rfc3514.txt>.



    147

    В XML-жаргоне то, что здесь названо "диалектом" (dialect), называется "приложением" (application). Автор избегает использования слова "приложение", поскольку в данном случае оно противоречит другому более широко распространенному его значению.



    148

    В течение нескольких лет казалось, что семиуровневый стандарт ISO может успешно конкурировать с набором протоколов TCP/IP. Он продвигался Европейским комитетом стандартов, напуганным мыслью о заимствовании любой технологии, рожденной в недрах Пентагона. Увы, их негодование превысило остроту их технического зрения. Результат оказался чрезмерно сложным и напрасным. Более подробно эта тема описана в книге [60].



    149

    Это название — дань кинофильму, вышедшему в 1958 году, который вошел в историю как "наихудшее из созданного", "Plan 9 from Outer Space". Документацию, включая обзорную статью, описывающую архитектуру, наряду с полным исходным кодом и дистрибутивом, который инсталлируется на PC, можно без труда найти с помощью Web-поиска по фразе "Plan 9 from Bell Labs".



    150

    История о том, как была создана UTF-8, включает в себя описание безумной ночной работы Кена Томпсона и Роба Пайка — <http://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt>.



    151

    Ищите

    F_NOTIFY
    в fcntl(2).



    152

    Данный параграф основывается на аналитической статье Генри Спенсера, вышедшей в 1984 году. Он отметил, что управление задачами было необходимо и целесообразно точно учесть в POSIX.1 и последующих стандартах Unix, поскольку оно "просачивается" в каждую программу и, следовательно, должно быть продумано в любом интерфейсе "приложение-система". Отсюда и одобрение POSIX ошибочной конструкции, когда правильные решения "выходили за рамки", а следовательно, даже не рассматривались.



    153

    Web-страница проекта screen(1) — http://www.math.fu-berlin.de/~guckes/screen/.



    154

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



    155

    http://www.cros-os.org/



    156

    Что же касается операционной системы Apple Newton, мини-компьютера AS/400 и карманного компьютера Palm, то здесь речь может идти об исключении.



    157

    Более полное обсуждение данного эффекта приведено в главе "The Magic Cauldron" книги [67].



    158

    Весьма пугающий перечень возможностей, созданный известным специалистом по безопасности, приведен в TCPA FAQ <http://www.cl.cam.ас.uk/~rjа14/tcpa-faq.html>.



    159

    Введение в гибкое программирование представлено на странице Agile Manifesto <http://agilemanifesto.org/>.








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх