Недоря А.Е. - Диссертация. Глава 3

Введение    Глава 1    Глава 2    Глава 3    Глава 4    Заключение    Литература    Приложения


3. Расширяемые системы на примере системы Оберон

В этой главе мы рассмотрим принципы построения РПС на примере системы Оберон [25]. Кроме собственно системы Оберон мы будем рассматривать расширяемый редактор Write [26], реализованный как одно из приложений системы.

Описывая систему Оберон мы постараемся определить базовые понятия и дать читателю основу для сравнения при описании системы Мифрил (гл. 4).

Разработка системы Оберон была начата Н.Виртом и Ю.Гуткнехтом в 1985 году. Целью проекта была разработка современной и переносимой ОС для персональных рабочих станций. Одновременно с разработкой системы был спроектирован и реализован язык Оберон. В первую очередь система и Оберон компилятор были реализованы для станций Ceres [27]. Далее был выполнен перенос на такие платформы, как Mac II [15], SparcStation/Unix [14], DecStation/Unix, i386/AIX, Chameleon [28]. На всех этих платформах (кроме Ceres и Chameleon) система Оберон не является операционной системой в традиционном смысле, а некоторым окружением, работающем над базовой ОС. Сравнение различных реализаций системы приводится в [29].

3.1. Обзор системы

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

Для работы система необходим графический экран и мышь. Экран разделен на вертикальные полосы (Tracks). Стандартная конфигурация экрана состоит из двух полос: пользовательской и системной. Каждая полоса, в свою очередь, состоит из нескольких окон (Viewer). Система не поддерживает концепцию перекрывающихся окон. Ширина всех окон равна ширине полосы, а при изменении вертикального размера некоторого окна происходит соответствующие изменения прилежащих окон. Полоса может быть целиком накрыта другой полосой. Такое строение окон является достаточно удобным, хотя и непривычным. Заметим только, что для экранов небольших размеров такая структура окон является мало пригодной.

При запуске системы пользователь видит на экране окно стандартного вывода и окно (System.Tool), содержащее набор основных команд (в том числе команду создания нового окна). Каждая команда M.P состоит из имени модуля M и имени процедуры без параметров P. Пользователь запускает команду, нажимая среднюю кнопку мыши на тексте команды. При активации команды система вызывает процедуру P из модуля M (загружая модуль M, если этот модуль не был загружен в систему).

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

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

В любом текстовом окно пользователь может написать текст команды (M.P) и после этого запустить ее. Стандартный набор команд для некоторой деятельности может быть записан в файл. После чего достаточно открыть окно, изображающее этот файл, для того чтобы исполнять команды. Система содержит набор таких файлов, содержащих команды (tool packages):

Edit.Tool     - создание и редактирование текстовых окон;

Draw.Tool     - создание и редактирование графических окон;

System.Tool   - определение и изменение конфигурации, файловые операции, ...;

Compiler.Tool - Оберон компилятор;

Net.Tool      - работа с сетью и электронной почтой.


Заметим, что все эти файлы - это просто текстовые файлы с именами команд.

3.2. Структура системы

Система Оберон состоит из следующих компонент: внутреннее ядро, внешнее ядро, подсистемы текстов, графики, рисунков и набора утилитных модулей (tools). Утилитный модуль - это модуль, реализующий набор команд.

  утилитные модули  
текстовая подсистема графическая подсистема подсистема рисунков
  Oberon  
  внешнее ядро  
  внутреннее ядро  

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

Каждая подсистема состоит из трех модулей. Рассмотрим структуру подсистемы на примере подсистемы текстов. Она состоит из следующих модулей:

Texts      - определяет понятие текста;

TextFrames - определяет понятие текстового окна;

Edit       - утилитный модуль.

Модуль Texts определяет тип и набор базовых операций над ним. Модуль TextFrames реализует текстовое окно и операции редактирования. Модуль Edit содержит набор команд открытия/записи и дополнительные операции редактирования.

Как и в любой другой системе, в системе Оберон имеется набор приложений, увеличивающих функциональные возможности. Важно заметить, что в системе нет разницы между собственно системой и приложениями. Каждое приложение определяет свой набор команд, и различные приложения могут использоваться одновременно. Мы упомянем только некоторые из реализованных приложений: редактор гипертекста [30], расширяемый редактор [26], графический редактор и система трассировки [31], которые использовались при разработке рабочей станции Ceres-3.

3.3. Возможности расширения

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


 Реализация новых команд


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


MODULE World;

 

IMPORT Texts, Oberon;

 

VAR

  i: LONGINT;

  W: Texts.Writer;

 

PROCEDURE Hello*;

BEGIN

  Texts.WriteInt(W,i,0);

  Texts.WriteString(W,": Hello, world");

  Texts.WriteLn(W);

  Texts.Append(Oberon.Log,W.buf);

  INC(i);

END Hello;

 

BEGIN

  i:=1;

  Texts.OpenWriter(W); -- открыли буфер записи

  Texts.WriteString(W,"What a wonderful world!");

  Texts.WriteLn(W);

  Texts.Append(Oberon.Log,W.buf); -- добавили буфер к окну

                                  -- стандартного вывода

END World.


После компиляции модуля напишем в любом текстовом окне "World.Hello" и запустим команду. Модуль "World" загрузится и напечатает "What a wonderful world!" и "1: Hello, world" в окно стандартного вывода. При следующих запусках команды будем печататься только строка "Hello, world", префиксированная номером запуска. Имя команды может быть написано в любом месте, например последняя строка нашего модуля может выглядеть так:


END World.Hello (* It's fun, isn't it? *)


Если в модуле сделаны некоторые изменения, и он заново скомпилирован, можно запустить новую версию модуля, нажав комбинацию кнопок мыши, или предварительно исполнив команду выгрузки (System.Free). Каждый раз при загрузке модуля будет выполнена его инициализация.

Отметим несколько важных особенностей такого подхода:

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

 Расширение базовых понятий


Рассмотрим механизм расширения понятия на примере типа "фрейм" (Frame) системы Оберон. Определение нового (расширенного) типа фрейм является стандартным методом расширения системы. Система включает большой набор таких расширений. Следующая картинка показывает (неполное) дерево основных расширений (узел дерева имеет вид ИмяМодуля.ИмяТипа).


Viewers.Track    MenuViewers.Viewer      WriteFrames.Frame

           ^            ^                     ^
           Viewers.Viewer           TextFrames.Frame

                        ^           ^
                        Display.Frame

Другими расширениями типа Display.Frame являются

GraphicFrames.Frame и

PictureFrames.Frame.

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

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


TYPE

  Frame = POINTER TO FrameDesc;

  FrameDesc = RECORD

    dsc,next: Frame; (* son, brother *)

    X,Y,W,H: INTEGER;

    handle: Handler;

  END;

 

  Handler = PROCEDURE (Frame, VAR FrameMsg);

  FrameMsg = RECORD END;


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

Для определения сообщения нам достаточно расширить тип FrameMsg. Например, сообщение о вводе с клавиатуры или мыши определяется в модуле Oberon следующим образом:


  InputMsg = RECORD (Display.FrameMsg)

    id: INTEGER; (* operation *)

    modes,keys: SET; (* mouse *)

    X,Y: INTEGER; (* position *)

    ch: CHAR; (* character read *)

  END;

Здесь id может принимать значения 0 (мышь) и 1 (клавиатура).

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

PROCEDURE Handle(F: Display.Frame; VAR M: Display.FrameMsg);

BEGIN

  IF M IS Oberon.InputMsg THEN

  WITH M: Oberon.InputMsg DO

    IF M.id = 0 THEN (* mouse *)

    ...

    ELSIF M.id = 1 THEN (* keyboard *)

    ...

    END;

  ELSIF M IS ...

  END;

END Handle;


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

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

PROCEDURE Broadcast(VAR M: Display.FrameMsg);

BEGIN

  FOR v IN all_visible_frames DO

    v.handle(v,M)

  END;

END Broadcast.


Вернемся к иерархии окон. Модуль Viewers определяет тип Viewer - базовое понятие окна, и тип Track - окно, заполняющее на экране вертикальную полосу. Модуль MenuViewers определяет окно стандартного вида, состоящего из двух фреймов:


               +-------------------+
               |       Меню        |
               +-------------------+
               |                   |
               |    Рабочее окно   |
               |                   |
               +-------------------+

Меню-фрейм является текстовым окном, а рабочее окно – любым расширением фрейма (например, текстовым, графическим и т.д.). Модуль TextFrames определяет понятие текстового фрейма, а модуль WriteFrames расширяет это понятие.

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

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

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

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


1) Элементы, определяющие структуру текста


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


2) Элементы – маркеры ошибок


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


3) Активные элементы (Clock Elements, Icon Elements)


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


4) Элементы - меню (PopUp Elements)


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


5) Графические элементы и элементы-рисунки


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


6) Структурные элементы (Fold Elements)


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


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


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

3.4. Анализ системы

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

Система Оберон фактически содержит один тип, являющийся базой для расширения (Frame). Остальные типы (Текст, Файл, Фонт) тоже могут быть расширены, но все операции над этими типами реализованы в виде статических процедур (не методов). Поэтому все операции над расширенным объектом должны импортироваться из другого модуля, и могут возникнуть некоторые проблемы при применении базовой операции к расширенному типу (что допускается правилами совместимости типов). Так например, если записать текст, содержащий элементы (WriteTextsxt) процедурой из модуля Texts, то все элементы будут утеряны. В документации по редактору Write перечислены все базовые операции, которые нельзя применять к расширенному типу. Повторим еще раз, что типовой контроль в данном случае невозможен. Реализация базовых понятий с помощью методов (Oberon-2 type-bound procedures) позволяет решить эту проблему, так как операция всегда соответствует типу (уровню расширения типа).

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

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


Введение    Глава 1    Глава 2    Глава 3    Глава 4    Заключение    Литература    Приложения