                                  
 

 
 
 
 
 
      Министерство приборостроения, средств автоматизации
 
                      и систем управления
 
 
 
 
 
 
 
 
 
                  Операционная система МОС ВП
 
                Подсистема разработки программ
 
           РУКОВОДСТВО ПО ПРОГРАММИРОВАНИЮ В СИСТЕМЕ
 
                      Справочный материал
 
 
 
 
                       Лист утверждения
 
                    2б.00152-01 97 09-1-ЛУ
 
                           Листов 2
 
 
                                  Руководитель темы 
 
                                  Зам. директора ИНЭУМ
 
                                  _______________ Хрущев С.Н.
 
                                  "___"__________ 1987 г.
 
                     Представители предприятия-разработчика:
 
                                  зав. отделом ВСКТБ
 
                                  _____________ Нахмансон В.С.
 
                                  "___"________ 1987 г.
 
                                  Зав. сектором ВСКТБ
 
                                  _____________ Жуков А. Н.
 
                                  "___"________ 1987 г.
 
 
                             1987
 
      Перв. примен.                                  Литера O
      2б.00152-01
 
 


                            - 2 -
 
                  2б.00152-01 97 09-1-ЛУ
 
 
 
 
 
 
 
 
 
                       Представители ИНЭУМ:
 
 
                               Ответственный исполнитель темы
 
                               Зав. отделением
 
                               -------------- Остапенко Г.П.
 
                               "---"--------- 1987 г.
 
                                Нормоконтролер
 
                                --------------
 
                                "---"--------- 1987 г.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                               
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 



 
 
 
 
 
 
 
        Утвержден
 
      2б.00152-01 97 09-1-ЛУ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                      Операционная система МОС ВП

                    Подсистема разработки программ
 
               РУКОВОДСТВО ПО ПРОГРАММИРОВАНИЮ В СИСТЕМЕ
 
                          Справочный материал
 
                         2б.00152-01 97 09-1
 
                             Листов 248/4К
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 
                                 1987
 
           Перв. примен.                            Литера O
           2б.00152-01
 



 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                      Операционная система МОС ВП
 
                    Подсистема разработки программ
 
               РУКОВОДСТВО ПО ПРОГРАММИРОВАНИЮ В СИСТЕМЕ
 
                          Справочный материал
 
                           00152-01 97 09-1
 
                              Листов 244
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                                 1987
 


                                - 2 -
 
                          00152-01 97 09-1
 
 
 
 
 
 
                              АННОТАЦИЯ
 
 
 
           Данный документ предназначен для руководства по  прог-
 
      раммированию в операционной системе с  виртуальной  памятью
 
      для ЭВМ СМ 1700 (МОС ВП).
 
           Информация, содержащаяся в  документе,  рассчитана  на
 
      опытных программистов, работающих с языком ФОРТРАН МОС  ВП,
 
      но может быть использована для работы с любым другим языком
 
      программирования в этой операционной системе.
 
           Данный документ состоит из трех частей.
 
           В первой части описаны структуры программных единиц  и
 
      связи между ними; приведен порядок  создания  и  управления
 
      процессами.
 
           Вторая часть содержит описание структур данных в прог-
 
      раммах, механизма создания команд  уровня  DCL  и  способов
 
      терминального ввода-вывода.
 
           Третья часть посвящена описанию способов  ввода-вывода
 
      при работе с файлами.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 3 -
 
                          00152-01 97 09-1
 
 
 
 
 
 
                              СОДЕРЖАНИЕ
 
 
 
 
 
      1.  Логика межпрограммных связей                        8
 
      1.1.  Вызов программных единиц                          11
 
      1.1.1.  Подпрограмма                                    11
 
      1.1.2.  Функция                                         12
 
      1.1.3.  Встроенные подпрограммы                         14
 
      1.1.4.  Системно-определенные процедуры                 15
 
      1.2.  Передача данных                                   17
 
      1.2.1.  Списки параметров                               17
 
      1.2.2.  Общие блоки памяти                              21
 
      1.2.2.1.  Применение                                    22
 
      1.2.2.2.  Инициализация                                 25
 
      1.3.  Передача управляющей информации                   26
 
      1.3.1.  Флаги                                           26
 
      1.3.2.  Маски                                           27
 
      1.3.3.  Указатели                                       28
 
      1.4.  Передача данных переменной длины                  30
 
      1.4.1.  Строки символов                                 31
 
      1.4.2.  Массивы                                         33
 
      1.5.  Передача параметров системным процедурам          37
 
      1.5.1.  Способы передачи параметров                     39
 
      1.5.2.  Выравнивание данных                             41
 
      1.5.3.  Передача байтов                                 42
 
      1.5.4.  Передача слов                                   42
 
      1.5.5.  Передача длинных слов                           43
 
      1.5.5.1.  Спецификация байтов и слов                    43


                                - 4 -
 
                          00152-01 97 09-1
 
 
 
      1.5.5.2.  Установка битов                               44
 
      1.5.6.  Передача квадрослов                             48
 
      1.5.6.1.  Задание байтов, слов и длинных слов           48
 
      1.5.6.2.  Установка битов                               52
 
      1.5.7.  Передача структур данных переменной длины       53
 
      1.6.  Чтение          информации,           возвращенной
 
            системно-определенной процедурой                  57
 
      1.6.1.  Маски                                           58
 
      1.6.2.  Адреса                                          60
 
      1.6.2.1.  Числовые данные                               60
 
      1.6.2.2.  Символьные данные                             60
 
      1.6.3.  Буферы                                          63
 
 
 
      2.  Организация программной единицы                     69
 
      2.1.  Локальная память                                  72
 
      2.1.1.  Определение типа данных                         73
 
      2.1.2.  Инициализация переменной                        75
 
      2.1.2.1.  Инициализация во время компиляции             75
 
      2.1.2.2.  Инициализация во время выполнения             77
 
      2.1.3.  Именованные константы                           78
 
      2.1.4.  Эквивалентные переменные                        79
 
      2.1.5.  Непрерывное расположение                        81
 
      2.1.6.  Большие структуры данных и динамическая память  81
 
      2.1.6.1.  Динамическое хранение числовых данных         82
 
      2.1.6.2.  Динамическое хранение символьных данных       85
 
      2.1.6.3.  Статистика динамической памяти                87
 
      2.2.  Последовательное выполнение                       88
 
      2.3.  Условное выполнение                               89
 
      2.3.1.  Задание условия                                 89
 


                                - 5 -
 
                          00152-01 97 09-1
 
 
      2.3.2.  Условный логический оператор                    91
 
      2.3.3.  Условные блочные операторы                      91
 
      2.3.3.1.  Исключающие условные блоки                    91
 
      2.3.3.2.  Включающие условные блоки                     95
 
      2.4.  Циклические операторы                             97
 
      2.4.1.  Оператор DO WHILE                               99
 
      2.4.2.  Оператор DO                                     100
 
      2.5.  Вложенные конструкции                             101
 
 
 
      3.  Синхронизация работы программ и связи между ними    104
 
      3.1.  Создание процесса                                 104
 
      3.1.1.  Создание подпроцесса                            105
 
      3.1.1.1.  Контекст подпроцесса                          108
 
      3.1.1.2.  Выполнение подпроцесса                        109
 
      3.1.1.3.  Отладка программы в подпроцессе               110
 
      3.1.2.  Синхронизация терминального ввода-вывода        111
 
      3.1.3.  Создание отсоединенного процесса                115
 
      3.1.4.  Выполнение программ по времени                  117
 
      3.1.4.1.  Запуск в заданное время                       118
 
      3.1.4.2.  Запуск через интервалы времени                119
 
      3.2.  Слежение за процессами                            122
 
      3.2.1.  Получение информации о процессе                 122
 
      3.2.2.  Слежение за выполнением процесса                128
 
      3.2.3.  Управление планированием процесса               129
 
      3.3.  Управление выполнением программы                  130
 
      3.3.1.  Синхронизация с помощью флагов событий          131
 
      3.3.1.1.  Флаги локальных событий                       132
 
      3.3.1.2.  Общие флаги событий                           134
 
      3.3.1.3.  Синхронные и асинхронные  служебные  системные
 
                процедуры                                     138


                                - 6 -
 
                          00152-01 97 09-1
 
 
 
      3.3.2.  Использование асинхронного системного прерывания
 
              (AST)                                           140
 
      3.4.  Обмен информацией между процессами                142
 
      3.4.1.  Идентификаторы и логические имена               143
 
      3.4.2.  Почтовые ящики                                  148
 
      3.4.2.1.  Синхронный ввод-вывод                         151
 
      3.4.2.2.  Непосредственный ввод-вывод                   155
 
      3.4.2.3.  Асинхронный ввод-вывод                        163
 
      3.4.3.  Разделяемые данные                              169
 
      3.4.3.1.  Установленные блоки общей памяти              169
 
      3.4.3.2.  Глобальные секции                             172
 
      3.5.  Сообщения внутри процесса                         180
 
      3.5.1.  Общие блоки процесса                            181
 
      3.5.2.  Передача управления                             182
 
 
 
      4.  Цикл подготовки                                     185
 
      4.1.  Трансляция и компоновка программ                  185
 
      4.1.1.  Создание исходной программы                     186
 
      4.1.2.  Трансляция программы                            188
 
      4.1.2.1.  Разработка систем                             190
 
      4.1.2.2.  Создание систем                               191
 
      4.1.2.3.  Ошибки                                        191
 
      4.1.3.  Об'ектные библиотеки                            194
 
      4.1.4.  Компоновка и выполнение программ                196
 
      4.1.5.  Привилегированные программы                     198
 
      4.1.6.  Построение программ                             199
 
      4.1.6.1.  Процесс построения                            200
 
      4.1.6.2.  Целостность системы                           201
 
      4.1.6.3.  Профилактическое копирование                  204
 


                                - 7 -
 
                          00152-01 97 09-1
 
 
      4.2.  Идентификаторы                                    210
 
      4.2.1.  Определение идентификаторов                     211
 
      4.2.2.  Локальные и глобальные идентификаторы           212
 
      4.2.3.  Ссылка на глобальные идентификаторы             213
 
      4.2.4.  Разрешение глобальных идентификаторов           214
 
      4.2.4.1.  Системные библиотеки по умолчанию             215
 
      4.2.4.2.  Библиотеки пользователя по умолчанию          216
 
      4.2.4.3.  Библиотеки макроопределений                   217
 
      4.2.5.  Библиотеки и файлы определений СП ФОРТРАН       218
 
      4.3.  Разделяемые образы                                220
 
      4.3.1.  Создание разделяемых образов                    220
 
      4.3.1.1.  Векторы передачи                              224
 
      4.3.1.2.  Возможность GSMATCH                           226
 
      4.3.1.3.  Возможность UNIVERSAL                         227
 
      4.3.2.  Библиотеки разделяемых образов                  228
 
      4.3.3.  Компоновка разделяемых образов                  229
 
      4.3.4.  Установка разделяемых образов                   231
 
      4.4.  Получение листингов                               231
 
      4.4.1.  Листинги компиляции                             231
 
      4.4.1.1.  Листинг исходной программы                    232
 
      4.4.1.2.  Печать об'ектных кодов                        234
 
      4.4.1.3.  Карта памяти                                  236
 
      4.4.1.4.  Характеристики компиляции                     240
 
      4.4.2.  Карты образов                                   241
 
 
 
      Перечень ссылочных документов                           244
 
      00152-01 97 09-2
 
      00152-01 97 09-3
 
 
 
 


                                - 7K -
 
                          00152-01 97 09-1
 
 
      4.2.  Идентификаторы                                    210
 
      4.2.1.  Определение идентификаторов                     211
 
      4.2.2.  Локальные и глобальные идентификаторы           212
 
      4.2.3.  Ссылка на глобальные идентификаторы             213
 
      4.2.4.  Разрешение глобальных идентификаторов           214
 
      4.2.4.1.  Системные библиотеки по умолчанию             215
 
      4.2.4.2.  Библиотеки пользователя по умолчанию          216
 
      4.2.4.3.  Библиотеки макроопределений                   217
 
      4.2.5.  Библиотеки и файлы определений СП ФОРТРАН       218
 
      4.3.  Разделяемые образы                                220
 
      4.3.1.  Создание разделяемых образов                    220
 
      4.3.1.1.  Векторы передачи                              224
 
      4.3.1.2.  Возможность GSMATCH                           226
 
      4.3.1.3.  Возможность UNIVERSAL                         227
 
      4.3.2.  Библиотеки разделяемых образов                  228
 
      4.3.3.  Компоновка разделяемых образов                  229
 
      4.3.4.  Установка разделяемых образов                   231
 
      4.4.  Получение листингов                               231
 
      4.4.1.  Листинги компиляции                             231
 
      4.4.1.1.  Листинг исходной программы                    232
 
      4.4.1.2.  Печать об'ектных кодов                        234
 
      4.4.1.3.  Карта памяти                                  236
 
      4.4.1.4.  Характеристики компиляции                     240
 
      4.4.2.  Карты образов                                   241
 
 
                               
      Перечень ссылочных документов                           244K
 
      00152-01 97 09-2
 
      00152-01 97 09-3
 
 
 


                                - 8 -
 
                          00152-01 97 09-1
 
 
                   1. Логика межпрограммных связей
 
 
 
           Выполняемая программа  (называемая  также  выполняемым
 
      образом, прикладной системой или  подсистемой)  состоит  из
 
      отдельных программных единиц, скомпилированных и  скомпоно-
 
      ванных в единое целое.  Одна  из  этих  программных  единиц
 
      является головной программой, а остальные - подпрограммами.
 
      Как указано в документе [1],  выполняемая  программа  вызы-
 
      вается на уровне языка команд DCL указанием ее имени,  либо
 
      с помощью команды RUN.
 
           На этапе проектирования программы определяется,  какие
 
      действия онA будет выполнять. Обычно  на  начальных  этапах
 
      разработки каждая программная  единица  выполняет  одно  из
 
      этих действий. По мере дальнейшей разработки программы соз-
 
      даются дополнительные подпрограммы для  реализации  сложных
 
      или повторяющихся операций. Каждая подпрограмма может  быть
 
      представлена в виде отдельного файла, либо группа  взаимос-
 
      вязанных подпрограмм может быть об'единена в один файл.
 
           При вызове выполняемой программы ее  выполнение  начи-
 
      нается с первого выполняемого оператора головной программы.
 
      Головная программа начинается оператором PROGRAM (не обяза-
 
      тельный, но рекомендуемый оператор) и кончается  оператором
 
      END; она вызывает подпрограммы, которые,  в  свою  очередь,
 
      могут вызывать другие подпрограммы. Подпрограмма может быть
 
      одного из следующих видов:
 
           1) подпрограмма. Програмная единица, начинающаяся опе-
 
      ратором SUBROUTINE и кончающаяся оператором END. Для вызова
 
      подпрограммы используется оператор CALL;
 
           2) функция. Програмная единица, начинающаяся  операто-
 
      ром FUNCTION и кончающаяся оператором END. Функция передает


                                - 9 -
 
                          00152-01 97 09-1
 
 
      в вызывающую программу значение заданного типа. Для  вызова
 
      функции используется ее  имя  в  любом  месте,  где  должно
 
      использоваться значение этого типа.
 
           Для получения ясной и  эффективной  программы  исполь-
 
      зуются системно-определенные процедуры и процедуры, опреде-
 
      ленные языком ФОРТРАН:
 
           1) процедуры библиотеки исполнительных процедур и обс-
 
      луживающие  процедуры,  описанные  в  [2],  [3].  Системно-
 
      определенные подпрограммы, выполняющие системные и обслужи-
 
      вающие действия, такие, как ввод-вывод с терминала;
 
           2) системные обслуживающие программы, описание которых
 
      приведено в [4]. Системно-определенные подпрограммы, такие,
 
      как определение программ обслуживания ошибок;
 
           3) встроенные подпрограммы. Подпрограммы, определенные
 
      в  языке  ФОРТРАН,  выполняющие  арифметические   операции,
 
      преобразование типов данных и операции с символами и  бита-
 
      ми.
 
           Процедуры библиотеки исполнительных процедур, в  отли-
 
      чие от процедур системного обслуживания, обычно  используют
 
      для передачи параметров способ,  принятый  по  умолчанию  в
 
      языке ФОРТРАН. Если процедура из библиотеки  исполнительных
 
      процедур и процедура системного обслуживания выполняют одну
 
      и ту же функцию, то процедура из библиотеки  исполнительных
 
      процедур предпочтительнее.
 
           На рис.1 показано разделение функции между программны-
 
      ми единицами внутри выполняемой программы.  Приводимая  для
 
      примера  программа  INCOME  читает  статистические   данные
 
      (GET_STATS),  накапливает  их  (REPORT)  и  исправляет   их
 
      (FIX_STATS). Сложные операции каждой из этих программ верх-
 
      него уровня выделены в подпрограммы (GET_1_STAT). Некоторые


                                - 10 -
 
                          00152-01 97 09-1
 
 
      из  программных   единиц   могут   использовать   системно-
 
      определенные процедуры для выполнения  обслуживающих  функ-
 
      ций, таких, как терминальный ввод-вывод.
 
 
         Структура программы INCOME (пример)
 
                         ------------
                         !          !
                         !  INCOME  !
                         !          !
                         ------------
                               !
                               !
                               !
              ------------------------------------
              !                !                 !
              !                !                 !
              !                !                 !
         -------------   ------------      -------------
         !           !   !          !      !           !
         ! GET_STATS !   !  REPORT  !      ! FIX_STATS !
         !           !   !          !      !           !
         -------------   ------------      -------------
               !
               !
               !
          --------------------
          !                  !
          !                  !
          !                  !
      --------------         !
      !            !         !
      ! GET_1_STAT !         !
      !            !         !
      --------------         !
           !                 !
           !                 !
           !                 !
      -----------------------------------------------
      !                                             !
      !    Другие пользовательские или системные    !
      !                                             !
      !               подпрограммы                  !
      !                                             !
      -----------------------------------------------
 
 
 
 
                                Рис. 1
 


                                - 11 -
 
                          00152-01 97 09-1
 
 
           1.1. Вызов программных единиц
 
 
           При вызове выполняемой программы с  командного  уровня
 
      DCL выполнение начинается с первого выполняемого  оператора
 
      (входной точки) головной программы. Оператор  END  головной
 
      программы прекращает выполнение, если  оно  не  закончилось
 
      ранее из-за фортранного оператора STOP  или  ошибки,  после
 
      которой продолжение невозможно, или  аварийного  завершения
 
      задачи. Если программная единица  (головная  программа  или
 
      подпрограмма) вызывает подпрограмму, управление  передается
 
      первому выполняемому оператору вызванной подпрограммы. Ког-
 
      да подпрограмма доходит до  оператора  END,  ее  выполнение
 
      прекращается и управление передается вызывающей программе -
 
      оператору, следующему  за  вызовом  подпрограммы  (так  как
 
      ENTRY и альтернативные возвраты вида "RETURN  метка"  нару-
 
      шают этот порядок выполнения операторов,  они  не  рекомен-
 
      дуются).
 
 
 
           1.1.1. Подпрограмма
 
 
           Для вызова подпрограммы  используется  оператор  CALL.
 
      Аргументы подпрограмме можно  передать  либо  через  список
 
      параметров, либо через блок общей памяти. Например,  опера-
 
      тор CALL вызывает подпрограмму GET_1_STAT и передает ей три
 
      аргумента через список параметров:
 
 
       CALL GET_1_STAT (LINE_NO,
 
       2                COL_PERSONS,
 
       2                PERSONS)
 
 
           Заголовок подпрограммы GET_1_STAT имеет вид:
 


                                - 12 -
 
                          00152-01 97 09-1
 
 
       SUBROUTINE GET_1_STAT (LINE_NO,    ! Входной параметр
 
       2                      COLUMN_NO,  ! Входной параметр
 
       2                      STAT)       ! Выходной параметр
 
       !
 
       ! Выполняемые операторы
 
       !
 
       END  ! Конец подпрограммы .
 
 
 
           1.1.2. Функция
 
 
           Функция передает в вызывающую программу значение опре-
 
      деленного типа. Для вызова функции  применяется  ссылка  на
 
      нее всюду, где только может использоваться значение данного
 
      типа. Ссылка на функцию состоит из имени функции, за  кото-
 
      рым следует список параметров (список параметров дает  воз-
 
      можность отличать функцию от переменной, поэтому он необхо-
 
      дим даже в том  случае,  когда  параметры  не  передаются).
 
      Аргументы можно передать функции  через  список  параметров
 
      или через блок общей памяти.
 
           В процессе выполнения подпрограммы-функции имени  этой
 
      функции присваивается значение, которое передается в  вызы-
 
      вающую программу. Тип функции, определяющий тип  ее  значе-
 
      ния, должен быть определен в самой функции и  в  вызывающей
 
      программе (эти определения должны совпадать). В подпрограм-
 
      ме-функции определение  типа  должно  предшествовать  слову
 
      FUNCTION. В вызывающей программе определение  типа  функции
 
      производится включением имени функции в  оператор  описания
 
      типа. Если в вызывающей программе имя функции не  присутст-
 
      вует в операторе описания типа, то тип функции определяется
 
      неявно.
 


                                - 13 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Вызов по ссылке функции GET_QUADAREA. Значением  функ-
 
      ции будет площадь четырехугольника (значение AREA, присвае-
 
      мое имени функции, есть площадь четырехугольника):
 
 
 
       REAL GET_QUADAREA,
 
       2    QUADAREA,
 
       2    SIDES (4), ANCLES (4)
 
       QUADAREA = GET_QUADAREA (SIDES,
 
       2                         ANCLES)
 
       ! Текст подпрограммы-функции GET_QUADAREA:
 
       REAL FUNCTION GET_QUADAREA (SIDES,  ! Передаваемый
 
       !                                   ! аргумент
 
       2                           ANCLES) ! Передаваемый
 
       !                                   ! аргумент
 
       ! Формальные параметры
 
       REAL SIDES(4),
 
       2    ANCLES(4)
 
       REAL AREA
 
       ! Вычисление площади:
 
       ! Выполняемые операторы
 
       !
 
       ! Имени функции присвоить значение площади
 
       GET_QUADAREA = AREA
 
       END   ! Конец подпрограммы-функции .
 


                                - 14 -
 
                          00152-01 97 09-1
 
 
           1.1.3. Встроенные подпрограммы
 
 
           Встроенные подпрограммы ФОРТРАНа, большинство из кото-
 
      рых являются функциями, выполняют арифметические  операции,
 
      преобразование типов данных, а также операции с символами и
 
      битами. Если  пользовательская  подпрограмма  и  встроенная
 
      подпрограмма имеют одно и то же имя, то предпочтение  полу-
 
      чает пользовательская подпрограмма (если  вызывающая  подп-
 
      рограмма определила ее в операторе EXTERNAL).
 
           Встроенную функцию нет необходимости задавать в опера-
 
      торе описания типа данных. Кроме того, неявное  определение
 
      типов данных (принятое по умолчанию или установленное  опе-
 
      ратором IMPLICIT) не влияет на тип встроенной функции.
 
           Каждая встроенная функция может иметь  групповое  имя,
 
      индивидуальное имя, или оба одновременно.
 
           Индивидуальное имя определяет тип данных параметров  и
 
      значения функции (если они есть).
 
           Групповое имя  дает  возможность  определить  по  типу
 
      параметров, какая индивидуальная программа из данной группы
 
      должна выполняться.
 
           Обычно при вызове встроенной функции  используется  ее
 
      групповое имя. Если же  указывается  встроенная  функция  в
 
      качестве фактического параметра  подпрограммы,  то  следует
 
      указывать ее индивидуальное имя. Например, функция с  инди-
 
      видуальным именем JIABS (групповое имя  ABS)  будет  выпол-
 
      няться дважды. В первый раз, поскольку  выполняется  JIABS,
 
      параметр должен быть значением типа INTEGER*4.  При  втором
 
      вызове, так как  параметр  есть  значение  типа  INTEGER*4,
 
      будет выполняться функция JIABS:
 


                                - 15 -
 
                          00152-01 97 09-1
 
 
       INTEGER*4 NUM,
 
       2         RESULT
 
       !
 
       ! Выполняемые операторы
 
       !
 
       RESULT = JIABS (NUM)
 
       RESULT = ABS (NUM)
 
 
 
           1.1.4. Системно-определенные процедуры
 
 
           Системно-определенные процедуры включают в себя проце-
 
      дуры из библиотеки исполнительных процедур  [2],  системные
 
      служебные процедуры [3] и обслуживающие процедуры [4].
 
           Обычно системно-определенные процедуры называются сок-
 
      ращением имени той группы, к которой  они  принадлежат;  за
 
      этим сокращением следует знак денежной единицы и описатель-
 
      ное имя.
 
           Стандартные сокращения названий групп процедур следую-
 
      щие:
 
         CLI - процедуры редактора командного языка;
 
         DCX - процедуры уплотнения данных;
 
         EDT - процедуры редактора;
 
         FDL - процедуры определения файла;
 
         LIB - общие обслуживающие процедуры;
 
         LBR - библиотечные процедуры;
 
         MTH - математические процедуры;
 
         SMG - процедуры управления экраном;
 
         SOR - процедуры сортировки и слияния;
 
         SYS - системные служебные процедуры.
 
           Большая часть системно-определенных процедур  написанA
 


                                - 16 -
 
                          00152-01 97 09-1
 
 
      в виде процедур-функций, причем значение функции  есть  код
 
      состояния (завершения) процедуры (значение типа  INTEGER*4,
 
      индицирующее успешное или аварийное завершение  программы).
 
      Для получения доступа к коду состояния  необходимо  опреде-
 
      лить тип имени процедуры как INTEGER*4 и вызывать процедуру
 
      как функцию. При вызове системно-определенных программ мож-
 
      но пользоваться и оператором CALL; процедура будет выполне-
 
      на, но ее код состояния невозможно будет проанализировать.
 
 
           Пример.
 
           Вызов процедуры LIB$GET_INPUT из  библиотеки  исполни-
 
      тельных процедур для чтения входной строки с экрана  терми-
 
      нала:
 
       INTEGER*4      STATUS,
 
       2              LIB$ERASE_PAGE
 
       CHARACTER*255  STRING
 
       INTEGER*4      STRING_LEN
 
       STATUS = LIB$GET_INPUT (STRING,
 
       2                       'ввод строки:',
 
       2                       STRING_LEN)
 
 
           Возможные коды состояния процедуры приводятся в  доку-
 
      менте [5] после  описания  правил  вызова  этой  процедуры.
 
      Вообще   говоря,   коды   завершения   являются   системно-
 
      определенными глобальными идентификаторами,  имена  которых
 
      составлены из сокращения названия  группы,  к  которой  они
 
      принадлежат, за которым стоит знак денежной единицы,  затем
 
      символ подчеркивания и сокращение сообщения об ошибке (коды
 
      состояния системных служебных программ в качестве  сокраще-
 
      ния названия группы используют SS, а не SYS):
 


                                - 17 -
 
                          00152-01 97 09-1
 
 
           LIB$_INVARG  -  общие обслуживающие программы:
 
                           неразрешенный аргумент;
 
           SS$ACCVIO    -  системные служебные программы:
 
                           нарушение доступа.
 
           Некоторые из процедур библиотеки исполнительных проце-
 
      дур передают в вызывающую программу  значение  функции,  не
 
      являющееся кодом состояния (см.  [5]).  Например,  значение
 
      функции LIB$LP_LINES есть принятое по умолчанию число строк
 
      на странице устройства печати.
 
           Процедура из библиотеки исполнительных  процедур,  чье
 
      значение не есть код состояния (завершения),  должна  вызы-
 
      ваться как функция, например:
 
       INTEGER*4 LENGTH,
 
       2         LIB$LP_LINES
 
       LENGTH = LIB$LP_LINES ()
 
 
 
           1.2. Передача данных
 
 
           Обычно для передачи данных между вызывающей и  вызван-
 
      ной программой  используется  список  параметров  или  блок
 
      общей памяти. Для получения ясной и  эффективной  программы
 
      применяется, если только возможно, список параметров.
 
 
 
           1.2.1. Списки параметров
 
 
           Список параметров, состоящий из одного или более пере-
 
      менных или констант, заключенных в скобки, должен быть опи-
 
      сан в двух местах:
 
           1) оператор вызова подпрограммы. Список  параметров  в
 
      операторе вызова передает в подпрограмму фактические  пара-
 
      метры. Параметры в этом списке так и называются фактически-


                                - 18 -
 
                          00152-01 97 09-1
 
 
      ми параметрами;
 
           2) оператор  определения  (описания)  подпрограммы.  В
 
      списке параметров в операторе определения подпрограммы так-
 
      же перечисляются параметры,  используемые  в  подпрограмме.
 
      Параметры этого списка называются формальными параметрами.
 
           При вызове подпрограммы фактические параметры подстав-
 
      ляются на место формальных  параметров  на  основе  взаимно
 
      однозначного соответствия. Первый фактический параметр свя-
 
      зыается с первым формальным параметром, второй - со  вторым
 
      и т.д. При выходе из подпрограммы каждый фактический  пара-
 
      метр имеет значение соответствующего ему формального  пара-
 
      метра. Так как фактические и  формальные  параметры  должны
 
      находиться во взаимно однозначном соответствии, они  должны
 
      быть согласованы по количеству, по порядку и по типу.
 
           Фактические параметры могут быть переменными, констан-
 
      тами, выражениями и именами подпрограмм. Любой  фактический
 
      параметр, отвечающий изменяемому в подпрограмме формальному
 
      параметру, должен быть переменной.
 
           Если в качестве  фактического  параметра  используется
 
      имя подпрограммы, то эта подпрограмма должна быть описана в
 
      операторе EXTERNAL или INTRINSIC,  это  означает,  что  имя
 
      есть глобальный идентификатор, определяемый в другой  прог-
 
      раммной единице.
 
           Системно-определенные  подпрограммы   и   подпрограммы
 
      пользователя описываются в операторе  EXTERNAL,  встроенные
 
      подпрограммы языка ФОРТРАН - в операторе INTRINSIC. Исполь-
 
      зуя встроенные подпрограммы в качестве фактических парамет-
 
      ров, следует употреблять не групповые имена, а индивидуаль-
 
      ные. Обычно нет необходимости описывать подпрограммы в опе-
 
      раторах EXTERNAL или INTRINSIC, поскольку любое имя, встре-


                                - 19 -
 
                          00152-01 97 09-1
 
 
      тившееся в операторе CALL, считается глобальным идентифика-
 
      тором.
 
           Формальные параметры - всегда переменные. Для  нагляд-
 
      ности им следует давать  имена,  отражающие  их  содержание
 
      (технически же имена фактических и формальных параметров не
 
      имеют значения).
 
 
           Пример.
 
           ПодпрограммA GET_1_STAT, имеющая три формальных  пара-
 
      метра LINE_NO, COLUMN_NO и STAT,  позиционирует  курсор  на
 
      экране согласно параметрам LINE_NO и  COLUMN_NO,  считывает
 
      значение в этой позиции и присваивает его формальному пара-
 
      метру STAT. Когда программа GET_STATS вызывает подпрограмму
 
      GET_1_STAT, она передает в эту подпрограмму три фактических
 
      параметра: LINE_NO, COL_PERSONS и PERSONS. Первые два пара-
 
      метра могут быть переменными, выражениями или  константами,
 
      лишь бы каждый  аргумент  вычислялся  как  значение  целого
 
      типа. Третий фактический параметр должен  быть  переменной,
 
      так как он меняется внутри подпрограммы:
 
 
       ! Текст программы GETSTATS.FOR
 
       ! Описание данных
 
       INTEGER*4 LINE_NO,
 
       2         COL_PERSONS,
 
       2         PERSONS
 
       PARAMETER (COL_PERSONS = 12)
 
       INTEGER*4 STATUS,
 
       2         GET_1_STAT
 
       EXTERNAL GET_1_STAT
 
       !
 
       ! Выполняемые операторы


                                - 20 -
 
                          00152-01 97 09-1
 
 
       !
 
       STATUS = GET_1_STAT (LINE_NO,     ! Первый фактический
 
       2                    COL_PERSONS, ! параметр, второй
 
       2                    PERSONS)     !         , третий
 
       !
 
       !
 
       ! Подпрограмма-функция GET1STAT.FOR
 
       INTEGER FUNCTION GET_1_STAT (LINE_NO,   ! 1,2,3
 
       2                            COLUMN_NO, ! Формальные
 
       2                            STAT)      ! параметры
 
       ! Спецификация формальных параметров
 
       INTEGER*4 LINE_NO,
 
       2         COLUMN_NO,
 
       2         STAT
 
       INTEGER*4 STATUS
 
       !
 
       ! Выполняемые операторы
 
       !
 
       GET_1_STAT = STATUS
 
       END
 
 
           При использовании  подпрограммы  необходимо  тщательно
 
      проверять список параметров. Если фактические параметры  не
 
      согласованы с формальными по количеству, порядку или  типу,
 
      подпрограмма получит неверные данные.  Получившая  неверные
 
      данные подпрограмма может закончить  работу  нормально,  но
 
      выдать при этом неверный  результат,  либо  может  породить
 
      ошибку (завершить работу аварийно). На ранних стадиях  раз-
 
      работки программы ошибка нарушения доступа обычно указывает
 
      на несогласованность фактических параметров с формальными.
 


                                - 21 -
 
                          00152-01 97 09-1
 
 
           1.2.2. Общие блоки памяти
 
 
           Оператор COMMON связывает список переменных  с  непре-
 
      рывной областью памяти, называемой блоком общей  памяти.  В
 
      программе может существовать один неименованный блок  общей
 
      памяти и сколько угодно  именованных  таких  блоков.  Когда
 
      различные программы обращаются к одному  и  тому  же  блоку
 
      общей памяти, они обращаются к одной и той же области памя-
 
      ти. Первая программа, обращающаяся к  некоему  блоку  общей
 
      памяти, его определяет.
 
           Если блок общей памяти является  именованным,  то  его
 
      имя, заключенное  между  двумя  наклонными  чертами  дроби,
 
      должно стоять сразу за ключевым словом  COMMON.  Имя  блока
 
      общей памяти есть глобальный идентификатор, который  должен
 
      отличаться от других глобальных идентификаторов, используе-
 
      мых в программе  (имена  подпрограмм,  другие  блоки  общей
 
      памяти и т.д.). Например, оператор COMMON  определяет  блок
 
      общей памяти размером четыре байта с именем UIC:
 
       INTEGER*2 MEMBER,
 
       2         GROUP
 
       COMMON /UIC/ MEMBER,
 
       2            GROUP
 
           Переменную, перед  тем  как  указать  ее  в  операторе
 
      COMMON, следует явно описать, иначе ей будет присвоен неяв-
 
      ный тип. Поскольку любая программная  единица,  ссылающаяся
 
      на блок общей памяти, может модифицировать  данные  в  нем,
 
      константы  (включая  те,  которые  определяются  оператором
 
      PARAMETER) не могут быть помещены в блок общей памяти. Кро-
 
      ме того, в блок общей памяти не могут быть  помещены  имена
 
      подпрограмм.
 


                                - 22 -
 
                          00152-01 97 09-1
 
 
           Переменные занимают место  в  блоке  общей  памяти  (и
 
      соответственно  в  отведенной  под  него  памяти)  согласно
 
      порядку их появления в операторе COMMON. Например, перемен-
 
      ная MEMBER типа INTEGER*2 ссылается на  два  младших  байта
 
      блока общей памяти, а переменная GROUP типа INTEGER*2  ссы-
 
      лается на два старших  байта.  Другая  программная  единица
 
      может обращаться к данным из блока общей памяти UIC  как  к
 
      четырем отдельным байтам, двум  словам  (как  в  предыдущем
 
      примере), или одному длинному слову. Однако,  обычно  прог-
 
      раммные единицы, использующие один  и  тот  же  блок  общей
 
      памяти, используют переменные, согласованные по количеству,
 
      порядку и типу.
 
 
 
           1.2.2.1. Применение
 
 
           Блоки общей памяти применяются для совместного исполь-
 
      зования (разделения) одних и тех же данных различными прог-
 
      раммными единицами или процессами:
 
           1) разделение  данных  между  программными  единицами.
 
      Подпрограммы часто вызываются без параметров или с  единст-
 
      венным параметром. Для передачи в такие подпрограммы допол-
 
      нительных данных используются блоки общей памяти.  За  счет
 
      использования блоков общей памяти  две  подпрограммы  могут
 
      иметь доступ к одним и тем же данным без того,  чтобы  одна
 
      из подпрограмм вызывала другую или была ею  вызвана.  Такие
 
      передачи данных затрудняют  сопровождение  программы;  если
 
      этого не удается избежать, их надо очень тщательно докумен-
 
      тировать;
 
           2) разделение  данных  между  процессами.  Блок  общей
 
      памяти, используемый для разделения данных между  процесса-
 


                                - 23 -
 
                          00152-01 97 09-1
 
 
      ми, должен  быть  установлен  как  разделенный  разделяемый
 
      образ.
 
 
           Пример.
 
           Программа COUNTAL.FOR подсчитывает количество  различ-
 
      ных имен библиотечного модуля путем  вызова  процедуры  (из
 
      библиотеки исполнительных процедур) LBR$SEARCH, которая для
 
      увеличения счетчика вызывает написанную пользователем подп-
 
      рограмму SEARCH. Увеличение счетчика происходит при  каждом
 
      нахождении нового имени:
 
 
      ! Текст программы COUNTAL.FOR
 
      ! Описание переменной состояния
 
      INTEGER STATUS
 
      ! Описание индекса библиотеки
 
      INTEGER INDEX,
 
      2       GLOBAL_INDEX
 
      PARAMETER (GLOBAL_INDEX = 2)
 
      ! Описание аргументов модуля
 
      CHARACTER*31 MODNAME    ! Имя модуля
 
      INTEGER MODNAME_LEN,    ! Длина имени модуля
 
      2       TXTRFA (2),     ! Адрес записи в файле для текста
 
      2       COUNT           ! Счетчик различных имен
 
      ! Описание блока общей памяти
 
      COMMON /SEARCH_VAR/ COUNT ! Третья переменная, передаваемая
 
                                ! в SEARCH
 
      !
 
      !
 
      ! Процедура поиска
 
      EXTERNAL SEARCH
 
      INTEGER SEARCH


                                - 24 -
 
                          00152-01 97 09-1
 
 
      ! Начало программы
 
      !
 
      ! Поиск различных имен в индексе номер 2
 
      STATUS = LBR$SEARCH (INDEX,       ! Библиотечный индекс
 
      2                    GLOBAL_INDEX,! Главный индекс
 
      2                    TXTRFA,      ! Адрес записи в файле
 
      2                    SEARCH)      ! пользов. Процедура
 
                                        ! процедура
 
      TYPE *,'подсчет различных имен:', COUNT
 
      END  ! Подпрограммы
 
      !
 
      ! Текст подпрограммы-функции SEARCH.FOR
 
      INTEGER FUNCTION SEARCH (ALIASNAME,
 
      2                        RFA)
 
      ! Функция, вызванная из LBR$SEARCH для каждого нового
 
      ! имени, подсчитывает различные имена
 
      ! Заданные аргументы процедуры LBR$SEARCH
 
      CHARACTER*(*) ALIASNAME  ! Имя модуля
 
      INTEGER RFA (2)          ! Адрес записи в файле для модуля
 
      ! Аргументы, заданные пользователем
 
      COMMON /SEARCH_VAR/COUNT
 
      INTEGER STATUS_OK
 
      PARAMETER (STATUS_OK = 1)
 
      COUNT = COUNT + 1   ! Увеличить счетчик различных имен
 
      SEARCH = STATUS_OK  ! Возвратить в LBR$SEARCH код
 
                          ! нормального состояния
 
      END   ! Функции .
 


                                - 25 -
 
                          00152-01 97 09-1
 
 
           1.2.2.2. Инициализация
 
 
           Если переменным из блока общей памяти  надо  присвоить
 
      начальные значения, лучше всего  использовать  подпрограмму
 
      BLOCK DATA. Любая программа,  обращающаяся  к  блоку  общей
 
      памяти, может задать  начальные  значения  его  переменным,
 
      однако, если более чем одна  программная  единица  пытается
 
      инициализировать один и тот же блок общей памяти, результа-
 
      ты будут непредсказуемыми; они зависят  от  того,  в  каком
 
      порядке программные единицы были  скомпонованы.  Технически
 
      блок общей памяти, инициализированный  подпрограммой  BLOCK
 
      DATA, не отличается от блока общей памяти, инициализирован-
 
      ного любой другой программной единицей. Однако  использова-
 
      ние подпрограммы BLOCK DATA дает уверенность,  что  никакие
 
      две программы не будут инициализировать один и тот же  блок
 
      общей памяти.
 
           Подпрограмма BLOCK DATA  -  это  программная  единица,
 
      начинающаяся оператором BLOCK DATA и заканчивающаяся опера-
 
      тором END. Подпрограммы BLOCK DATA могут содержать описания
 
      типов,  операторы  PARAMETER,  COMMON,  EQUIVALENCE,  DATA,
 
      IMPLICIT, DIMENSION и SAVE; ни одного выполняемого операто-
 
      ра эти подпрограммы содержать не могут.
 
           Например, подпрограмма BLOCK DATA инициализирует  блок
 
      общей памяти с именем UIC. Чтобы получить  доступ  к  блоку
 
      общей памяти с именем UIC из  другой  программной  единицы,
 
      нужно описать в ней блок общей памяти с тем же именем:
 
 
      BLOCK DATA INIT_UIC
 
      ! Инициализация блока общей памяти UIC значением UIC
 
      ! [210,200]
 
      INTEGER*2     MEMBER /'200'0/,


                                - 26 -
 
                          00152-01 97 09-1
 
 
      2             CROUP /'210'0/
 
      COMMON /UIC/  MEMBER,
 
      2             CROUP
 
      END
 
 
 
           1.3. Передача управляющей информации
 
 
           Управляющая информация - это информация, которой обме-
 
      ниваются программные единицы для определения  логики  своей
 
      работы,  например,  последовательности,  в  которых  должны
 
      выполняться  операторы   (последовательность   выполнения).
 
      Обычно для передачи управляющей информации между программа-
 
      ми применяются флаги, маски и указатели.
 
           Управляющие переменные, используемые более  чем  одной
 
      программной единицей, должны об'являться глобальными.
 
 
 
           1.3.1. Флаги
 
 
           Флаг указывает на одно из двух условий. Для  представ-
 
      ления флага используется переменная типа LOGICAL. Например,
 
      в программе OPEN_FILE флаг READ_ONLY используется для опре-
 
      деления того, какой из опEраторов OPEN выполнять:
 
 
      SUBROUTINE OPEN_FILE (FILENAME,
 
      2                      READ_ONLY)
 
      ! Задать формальные параметры
 
      CHARACTER*(*) FILENAME
 
      LOGICAL READ_ONLY
 
      ! Логический номер устройства для файла
 
      INTEGER CHAP_LUN
 
      IF (READ_ONLY) THEN
 


                                - 27 -
 
                          00152-01 97 09-1
 
 
          OPEN (UNIT = CHAP_LUN,
 
      2         FILE = FILENAME,
 
      2         READONLY,
 
      2         STATUS = 'OLD')
 
      ELSE
 
          OPEN (UNIT = CHAP_LUN,
 
      2          FILE = FILENAME,
 
      2          STATUS = 'OLD')
 
      END IF
 
      END     ! Подпрограммы .
 
 
 
           1.3.2. Маски
 
 
           Маска означает одно или несколько условий  из  набора.
 
      Для маски используется длинное слово.  Каждый  бит  в  этом
 
      длинном слове  соответствует  одному  конкретному  условию;
 
      если это условие выполнено, то бит  установлен  в  единицу.
 
      Для наглядности  каждому  смещению  (биту)  устанавливается
 
      символическое имя с помощью оператора PARAMETER.
 
 
           Пример.
 
           В сегменте программы маска QUALIFIER указывает,  какие
 
      квалификаторы заданы в командной строке. Условные операторы
 
      проверяют, что выбранные квалификаторы не являются  взаимно
 
      исключающими:
 
      ! Маска
 
      INTEGER*4 QUALIFIER
 
      ! Смещения
 
      INTEGER*4 FORWARD,
 
      2         BACKWARD,
 
      2         GENERAL,


                                - 28 -
 
                          00152-01 97 09-1
 
 
      2         EXACT,
 
      2         BEGIN,
 
      2         END
 
      PARAMETER (FORWARD  = 1,
 
      2          BACKWARD = 2,
 
      2          GENERAL  = 3,
 
      2          EXACT    = 4,
 
      2          BEGIN    = 5,
 
      2          END      = 6)
 
      ! Коды условий
 
      EXTERNAL BAD_QUALIFIER
 
      !
 
      ! Выполняемые операторы
 
      !
 
      ! Квалификаторы /BEGIN и /END взаимно исключающщие
 
      ! Квалификаторы /GENERAL и /EXACT взаимно исключающие
 
      IF ((BTEST(QUALIFIER,BEGIN) .AND. BTEST(QUALIFIER,END)).OR.
 
         (BTEST(QUALIFIER,GENERAL) .AND. BTEST(QUALIFIER,EXACT)))
 
      2  CALL LIB$SIGNAL (%VAL(%LOC(BAD_QUALIFIER)))
 
      !
 
      ! Продолжение программы .
 
 
 
           1.3.3. Указатели
 
 
           Указатель соответствует в точности одному  условию  из
 
      набора. Для указателя используется переменная типа INTEGER,
 
      а для представления возможных условий - целые константы.
 
 
           Пример.
 
           Программном сегменте  указатель  COMMAND  используется
 
      для определения  того,  какая  программная  единица  должна


                                - 29 -
 
                          00152-01 97 09-1
 
 
      выполняться:
 
      ! Описание переменных
 
      !
 
      ! Введенная команда
 
      INTEGER COMMAND
 
      ! Возможные команды
 
      INTEGER COMMAND_CHAPTER,
 
      2       COMMAND_TC,
 
      2       COMMAND_INDEX,
 
      2       COMMAND_SEARCH
 
      PARAMETER (COMMAND_CHAPTER = 1,
 
      2          COMMAND_TC = 2,
 
      2          COMMAND_INDEX = 3,
 
      2          COMMAND_SEARCH = 4)
 
      !
 
      ! Выполняемые операторы
 
      !
 
      ! Принять команду
 
      STATUS = GET_COMMAND (COMMAND)
 
      ! Выполнение команд до EXIT
 
      DO WHILE (STATUS)
 
          IF (COMMAND .EQ. COMMAND_CHAPTER)
 
      2 STATUS = PERUSE_CHAPTER (BOOK,
 
      2                          CHAPTER_NO,
 
      2                          SECTION_NO,
 
      2                          BEGIN_TEXT,
 
      2                          END_TEXT)
 
        IF (COMMAND .EQ. COMMAND_TC)
 
      2 STATUS = PERUSE_TC (BOOK,
 
      2                     CHAPTER_NO,


                                - 30 -
 
                          00152-01 97 09-1
 
 
      2                     SECTION_NO,
 
      2                     BEGIN_TEXT,
 
      2                     END_TEXT)
 
      !
 
      ! Продолжение программы
 
      !
 
      ! Принять другую команду
 
        IF (STATUS) STATUS = GET_COMMAND (COMMAND)
 
      END DO
 
      !
 
      ! Продолжение программы .
 
 
 
           1.4. Передача данных переменной длины
 
 
           Если в качестве фактического параметра передается мас-
 
      сив или строка символов, то их длина должна  быть  указана.
 
      Если строки символов или массивы являются формальными пара-
 
      метрами, то их также  можно  описать,  используя  указанную
 
      длину. Поскольку пользователь может захотеть задавать  раз-
 
      личные фактические параметры при каждом вызове  подпрограм-
 
      мы, то строки символов передаваемой длины, массивы перемен-
 
      ной длины и массивы воспринимаемой длины позволяют включать
 
      массивы и строки символов в операторы описания типа в подп-
 
      рограмме без указания их длины.
 
           Область хранения  данных  закрепляется  за  формальным
 
      параметром (массив или строка символов) в тот момент, когда
 
      этот формальный параметр получает значение; область  хране-
 
      ния остается закрепленной за ним во  все  время  выполнения
 
      программы. Для операций  с  памятью  можно  воспользоваться
 
      библиотечными программами LIB$GET_VM и LIB$FREE_VM, которые
 


                                - 31 -
 
                          00152-01 97 09-1
 
 
      занимают и освобождают память, используемую  подпрограммой.
 
      Занятие и освобождение памяти также рекомендуется для общих
 
      обслуживающих программ, поскольку неизвестно заранее, какую
 
      память может потребовать вызывающая программа.
 
 
 
           1.4.1. Строки символов
 
 
           Формальному параметру, описанному как строка  символов
 
      передаваемой длины, может отвечать любой фактический  пара-
 
      метр, имеющий тип CHARACTER. Чтобы указать, что  формальный
 
      параметр (строка символов) есть строка передаваемой  длины,
 
      надо в описании этого формального параметра в качестве дли-
 
      ны указать звездочку, заключенную в скобки:
 
       CHARACTER *(*) TITLE
 
           Формальный параметр типа CHARACTER воспринимает  длину
 
      фактического параметра типа CHARACTER. Чтобы избежать появ-
 
      ления нежелательных хвостовых пробелов в строке символов  в
 
      подпрограмме,  нужно  задавать  фактический  параметр   как
 
      подстроку.
 
 
           Пример.
 
           Вызывающая программа передает две подстроки в подпрог-
 
      рамму REPORT:
 
 
      ! Описание данных
 
      !
 
      CHARACTER*26 TITLE
 
      CHARACTER*10 FIRM
 
      INTEGER TITLE_LEN,
 
      2       FIRM_LEN
 
      INTEGER STATS (1020)
 


                                - 32 -
 
                          00152-01 97 09-1
 
 
      !
 
      ! Продолжение программы
 
      !
 
      CALL REPORT (TITLE(1:TITLE_LEN),
 
      2            FIRM(1:FIRM_LEN),
 
      2            STATS)
 
      !
 
      ! Продолжение программы .
 
 
           Соответствующие формальные  параметры  в  подпрограмме
 
      REPORT являются строками передаваемой длины (поскольку фор-
 
      мальные параметры связаны с подстроками, нет  необходимости
 
      в спецификации подстроки при передаче формального параметра
 
      TITLE в библиотечную процедуру LIB$PUT_OUTPUT).
 
           Подпрограмма REPORT имеет вид:
 
 
      SUBROUTINE REPORT (TITLE,
 
      2                  FIRM,
 
      2                  STATS)
 
      CHARACTER*(*) TITLE,
 
      2              FIRM
 
      INTEGER STATS (1020)
 
      !
 
      !
 
      ! Писать заголовок
 
      STATUS = LIB$PUT_OUTPUT (TITLE)
 
      !
 
      ! Продолжение подпрограммы .
 


                                - 33 -
 
                          00152-01 97 09-1
 
 
           1.4.2. Массивы
 
 
           Чтобы передать массив в качестве фактического парамет-
 
      ра, задается его имя.  Первый  элемент  массива,  заданного
 
      фактическим параметром, связывается с первым элементом мас-
 
      сива, заданного формальным параметром, второй - со вторым и
 
      т.д.
 
           Формальный параметр, определяющий массив, можно связы-
 
      вать с любым фактическим параметром того же типа. Если  нет
 
      каких-то особых соображений для изменения массива, то  раз-
 
      мерности формального массива желательно, за возможным  иск-
 
      лючением последней размерности, выдержать такими же, как  у
 
      фактического массива. Благодаря способу хранения массива  в
 
      памяти изменение размерностей массива влияет на  расположе-
 
      ние его элементов.
 
           Обычно формальные массивы об'являются массивами  пере-
 
      менной длины или массивами воспринимаемой длины.
 
           Формальный параметр  об'является  массивом  переменной
 
      длины путем использования  для  указания  его  размерностей
 
      формальных параметров и переменных типа  INTEGER  из  блока
 
      общей памяти. Для указания размерностей можно  использовать
 
      выражения, но в эти выражения должны  входить  только  фор-
 
      мальные параметры, переменные из блока общей памяти и конс-
 
      танты.
 
 
           Пример.
 
           Передача  подпрограмме   SEARCH   двумерного   массива
 
      NAME_LIST с указанием двух его размерностей ROWS и COLUMNS:
 
      ! описание массива и размерности
 
      CHARACTER*10 NAME_LIST (10,12)
 
      INTEGER*4       ROWS /10/,


                                - 34 -
 
                          00152-01 97 09-1
 
 
      2               COLUMNS /12/
 
      CHARACTER*10    NAME
 
      INTEGER*4       NAME_LEN
 
      !
 
      ! Продолжение программы
 
      !
 
      CALL SEARCH     (NAME_LIST,
 
      2               ROWS,
 
      2               COLUMNS,
 
      2               NAME,
 
      2               NAME_LEN)
 
      !
 
      ! Продолжение   программы .
 
           Подпрограмма SEARCH определяет формальный параметр как
 
      массив переменой длины:
 
      SUBROUTINE SEARCH (ARRAY,
 
      2                 ROWS,
 
      2                 COLUMNS,
 
      2                 KEYWORD,
 
      2                 KEYWORD_LEN)
 
      ! Поиск KEYWORD в ARRAY
 
      ! Спецификация параметров
 
      CHARACTER*10 ARRAY (ROWS, COLUMNS)
 
      INTEGER*4 LENGTH
 
      CHARACTER*10 KEYWORD
 
      INTEGER*4 KEYWORD_LEN
 
      !
 
      ! Продолжение подпрограммы
 
      !
 
      END   ! Конец подпрограммы SEARCH  .


                                - 35 -
 
                          00152-01 97 09-1
 
 
           Формальный параметр специфицируется как массив воспри-
 
      нимаемой длины заданием звездочки вместо последней  размер-
 
      ности массива. Если массив - формальный параметр - многоме-
 
      рен, то его размерности должны  совпадать  с  размерностями
 
      фактического параметра (за исключением, возможно, последней
 
      размерности).
 
 
           Пример.
 
           Передача в подпрограмму  SEARCH  массива  NAME_LIST  и
 
      положения последнего элемента в нем:
 
      ! Описание массива
 
      CHARACTER*10 NAME_LIST (12)
 
      INTEGER*4    LAST
 
      CHARACTER*10 NAME
 
      INTEGER*4    NAME_LEN
 
      !
 
      ! Продолжение описаний
 
      !
 
      ! Вызов SEARCH
 
      CALL SEARCH (NAME_LIST,
 
      2            LAST,
 
      2            NAME,
 
      2            NAME_LEN)
 
      !
 
      ! Продолжение
 
      !
 
           В подпрограмме SEARCH формальный параметр -  массив  -
 
      описан как массив с воспринимаемой длиной, а положение пос-
 
      леднего элемента служит для того, чтобы избежать выхода  за
 
      границы фактического массива:
 


                                - 36 -
 
                          00152-01 97 09-1
 
 
      SUBROUTINE SEARCH (ARRAY,
 
      2                  LAST,
 
      2                  KEYWORD,
 
      2                  KEYWORD_LEN)
 
      ! Поиск KEYWORD в ARRAY
 
      ! Формальные параметры
 
      CHARACTER*10 ARRAY (*)
 
      INTEGER*4    LAST
 
      CHARACTER*10 KEYWORD
 
      INTEGER*4    KEYWORD_LEN
 
      !
 
      ! Продолжение подпрограммы
 
      !
 
      END   ! Конец подпрограммы SEARCH  .
 
           Переменная длина массива и  воспроизводимая  длина  не
 
      являются  взаимно  исключающими.   Например,   подпрограмма
 
      SEARCH описывает формальный параметр - массив -  с  помощью
 
      формальных параметров по первым двум размерностям (перемен-
 
      ная длина) и звездочкой по третьей размерности (воспроизво-
 
      димая длина):
 
      SUBROUTINE SEARCH (ARRAY,
 
      2                  ROWS,
 
      2                  COLUMNS,
 
      2                  KEYWORD,
 
      2                  KEYWORD_LEN)
 
      ! Поиск KEYWORD в ARRAY
 
      ! Спецификация параметров
 
      CHARACTER*10 ARRAY (ROWS,COLUMNS,*)
 
      INTEGER*4    LENGTH
 
      CHARACTER*10 KEYWORD


                                - 37 -
 
                          00152-01 97 09-1
 
 
      INTEGER*4    KEYWORD_LEN
 
      !
 
      ! Продолжение
 
      !
 
      END   ! Конец подпрограммы SEARCH.
 
           Как правило, массивы  с  воспринимаемой  длиной  проще
 
      обрабатывать, чем массивы переменной длины,  в  силу  этого
 
      для одномерных массивов часто используются формальные пара-
 
      метры - массивы - с воспринимаемой длиной. Однако, посколь-
 
      ку подпрограмма не может вычислить длину у массива с  восп-
 
      ринимаемой длиной, приходится использовать массивы перемен-
 
      ной длины в таких обстоятельствах:
 
           - необходимо сослаться на массив целиком, включив  его
 
      имя в список ввода-вывода;
 
           - необходимо задействовать проверку  границ  для  фор-
 
      мального параметра - массива (FORTRAN/CHECK=BOUNDS).
 
 
 
           1.5. Передача параметров системным процедурам
 
 
           В документации МОС ВП (см. [2], [3], [4])  для  каждой
 
      системной процедуры указано, какие параметры ей  необходимо
 
      или возможно передать. Рекомендуется опускать любой  необя-
 
      зательный параметр, значение которого, принятое по  умолча-
 
      нию, является приемлемым. Вместо опущенного параметра вклю-
 
      чается запятая. При использовании процедуры  из  библиотеки
 
      исполнительных  процедур  [2]  или  обслуживающей  процеду-
 
      ры [3], заключительные запятые можно опустить, при  исполь-
 
      зовании служебных системных процедур [4] - нельзя.
 
           Если  необязательный  аргумент  опущен,  язык  ФОРТРАН
 
      использует механизм передачи параметра по значению, переда-
 


                                - 38 -
 
                          00152-01 97 09-1
 
 
      вая вместо опущенного аргумента значение 0. Если этот аргу-
 
      мент передается по ссылке или по дескриптору, то значение 0
 
      указывает  системно-определенной  процедуре,  что   следует
 
      использовать значение,  принятое  по  умолчанию  (если  оно
 
      существует). Если отсутвующий параметр передается по значе-
 
      нию, то системно-определенная процедура будет  использовать
 
      значение 0 вместо любого существующего по умолчанию  значе-
 
      ния.
 
           В описании параметров системных  программ  указывается
 
      метод доступа, тип данных, способ передачи и значение пара-
 
      метров. Использование этой информации изложено в [5].  Если
 
      системно-определенная процедура только читает данные  аргу-
 
      мента, фактический параметр может быть переменной,  выраже-
 
      нием или константой. Если же системно-определенная процеду-
 
      ра изменяет данные аргумента, то фактический параметр  обя-
 
      зан быть переменной.
 
           Описания процедур не являются специальными  для  языка
 
      фортран, поэтому тип данных параметров указывается по вели-
 
      чине. Табл. 1 устанавливает соответствие размерностей вели-
 
      чин с типами данных языка фортран. Если будет передан пара-
 
      метр неверного типа, сообщения об ошибке не будет, но  про-
 
      цедура получит неверные данные и ошибка может произойти  во
 
      время выполнения программы.
 
                                                        Таблица 1
 
      -----------------------------------------------------------
                          !
           Величина       !      Фортранный тип данных
                          !
      -----------------------------------------------------------
                          !
         Байт             !  BYTE
                          !
         Слово            !  INTEGER*2, LOGICAL*2
                          !
         Длинное слово    !  INTEGER*4, LOGICAL*4


                                - 39 -
 
                          00152-01 97 09-1
 
 
                                              Продолжение табл. 1
      -----------------------------------------------------------
                          !
           Величина       !      Фортранный тип данных
                          !
      -----------------------------------------------------------
                          !
         Квадрослово      !  INTEGER*4(2), LOGICAL*4(2)
                          !
         Строка символов  !  CHARACTER
                          !
         Имя программы    !  Имя подпрограммы
                          !
         Структура данных !  RECORD
                          !
       переменной длины   !
 
           Примечания:
 
           1. Так как многие процедуры не допускают  заключитель-
 
      ных пробелов, поэтому следует специфицировать подстроку,  а
 
      не строку полностью (тип CHARACTER).
 
           2. Имя подпрограммы, передаваемое в качестве  парамет-
 
      ра, должно быть описано в операторе EXTERNAL или INTRINSIC.
 
 
 
           1.5.1. Способы передачи параметров
 
 
           Параметры передаются одним их трех способов:
 
           1) по ссылке. Адрес  параметра  передается  в  длинном
 
      слове;
 
           2) по дескриптору. Адрес дескриптора  параметра  пере-
 
      дается в длинном слове. Формат дескриптора зависит от  типа
 
      параметра и может содержать два или более длинных слова;
 
           3) по значению. Значение аргумета передается в длинноM
 
      слове; это значение должно быть 32-х битным.
 
           В табл. 2 перечислены функции системы программирования
 
      (СП) ФОРТРАН, позволяющие  пользователю  задать  конкретный
 
      механизм передачи параметра. Обычно эти функции используют-
 
      ся для того, чтобы отметить действующий по умолчанию способ
 
      передачи.


                                - 40 -
 
                          00152-01 97 09-1
 
 
                                                        Таблица 2
 
      -----------------------------------------------------------
                         !
           Функция       !        Способ передачи
                         !
      -----------------------------------------------------------
                         !
        %REF             !   Передает параметр по ссылке
                         !
        %DESCR           !   Передает параметр по дескриптору
                         !
        %VAL             !   Передает параметр по значению. Если
                         !
                         ! фактический параметр короче 32 бит,
                         !
                         ! происходит распространение знака на
                         !
                         ! 32-х битовое поле
 
 
           Примечание. При распространении знака  (функция  %VAL)
 
      старшие разряды длинного поля заполняются значениями знако-
 
      вого разряда более короткого значения.
 
           Механизмы передачи параметров, используемые в вызываю-
 
      щей и вызываемой программе,  должны  быть  согласованы.  По
 
      умолчанию в СП ФОРТРАН принят способ передачи логических  и
 
      численных данных по ссылке и символьных данных по  дескрип-
 
      тору. Эти принятые по умолчанию правила полнOстью применимы
 
      при передаче параметров от одной программной единицы к дру-
 
      гой. При передаче параметров системно-определенным процеду-
 
      рам из этих правил могут быть исключения.
 
           Наиболее частое исключение из  принятых  по  умолчанию
 
      правил передачи параметров встречается тогда, когда систем-
 
      но-определенная процедура требует, чтобы  параметр  переда-
 
      вался по значению. Например, системная служебная  процедура
 
      SYS$CLREF требует одного параметра, и этот параметр  должен
 
      передаваться по значению:
 
      INTEGER*4 EV_FLAG
 
      !Описать состояние и системные процедуры
 


                                - 41 -
 
                          00152-01 97 09-1
 
 
      INTEGER*4 STATUS,
 
      2       SYS$CLREF
 
      STATUS=SYS$CLREF (%VAL(EV_FLAG))
 
 
 
           1.5.2. Выравнивание данных
 
 
           В редких случаях системно-определенная процедура  тре-
 
      бует, чтобы параметр был выравнен  по  границе  поля,  т.е.
 
      данные в памяти должны начинаться с границы  байта,  слова,
 
      длинного слова, квадрослова или страницы (в описании  прог-
 
      раммы  указано,  какой  тип  выравнивания  требуется).  Для
 
      выравнивания данных надо сделать следующее:
 
           1) поместить данные в блок общей памяти;
 
           2) создать файл возможностей,  содержащий  возможность
 
      компоновки PSECT_ATTR в виде:
 
      PSECT_ATTR = имя, выравнивание
 
      где имя          - имя блока общей памяти, содержащего
 
                         данные;
 
          выравнивание - одно из ключевых слов: BYTE, WORD,
 
                         LONG, QUAD или PAGE;
 
           3) скомпоновать файл возможностей со своей программой.
 
           Если предположить, например, что программа SECTION.OBJ
 
      содержит блок общей  памяти  с  именем  GLOBAL_SECTION,  то
 
      команда LINK выравнивает  данные  этого  блока  по  границе
 
      страницы:
 
      $ LINK SECTION,PAGE_ALIGN.OPT/OPTION
 
      файл возможностей PAGE_ALIGN.OPT содержит возможность  ком-
 
      поновщика PSECT_ATTR:
 
      PSECT_ATTR = CLOBAL_SECTION, PAGE
 


                                - 42 -
 
                          00152-01 97 09-1
 
 
           1.5.3. Передача байтов
 
 
           Для передачи адреса байта в системно-определенную про-
 
      цедуру (т.е. для передачи  байтового  значения  по  ссылке)
 
      фактический параметр задается как байтовое значение. Напри-
 
      мер, процедура LIB$DEC_OVER использует значение  переменной
 
      OVERFLOW типа BYTE для определения, задействовать или отме-
 
      нить распознавание десятичного переполнения:
 
      ! Параметр для LIB$DEC_OVER
 
      BYTE OVERFLOW /1/
 
      INTEGER OLD_OVERFLOW
 
      OLD_OVERFLOW = LIB$DEC_OVER (OVERFLOW)
 
 
 
           1.5.4. Передача слов
 
 
           Для передачи адреса параметра длиной в слово системно-
 
      определенной процедуре (т.е. для передачи словного значения
 
      по ссылке) задается фактический  параметр  как  двухбайтное
 
      целое значение. Например,  процедура  LIB$GET_INPUT  читает
 
      информацию с терминала и возвращает число прочитанных  сим-
 
      волов в переменной AGE_LEN типа INTEGER*2:
 
      ! Параметры для LIB$GET_INPUT
 
      CHARACTER*3 AGE
 
      INTEGER*2 AGE_LEN
 
      ! Описать состояние и системные процедуры
 
      INTEGER*4 STATUS,
 
      2       LIB$GET_INPUT
 
      STATUS = LIB$GET_INPUT (AGE,
 
      2       'ENTER AGE:'  ,         !подсказка
 
      2       AGE_LEN)                !длина строки AGE.
 


                                - 43 -
 
                          00152-01 97 09-1
 
 
           1.5.5. Передача длинных слов
 
 
           Чтобы передать в системно-определенную процедуру адрес
 
      параметра, занимающего  длинное  или  двойное  слово  (т.е.
 
      передать по ссылке  значение,  занимающее  длинное  слово),
 
      фактический параметр задается как четырехбайтное целое зна-
 
      чение. Например, процедура SYS$SUSPND приостанавливает про-
 
      цесс с  идентификатором,  специфицированным  значением  PID
 
      типа INTEGER*4:
 
      ! Параметр для SYS$SUSPND
 
      INTEGER PID
 
      ! Описать состояние и системные процедуры
 
      INTEGER*4 STATUS,
 
      2       SYS$SUSPND
 
      STATUS = SYS$SUSPND (PID,)
 
           Системно-определенные процедуры могут потребовать спе-
 
      цификации битов, байтов или  слов  внутри  длинного  слова-
 
      параметра.
 
 
 
           1.5.5.1. Спецификация байтов и слов
 
 
           Для спецификации байтов или слов внутри длинного слова
 
      нужно определить это длинное слово как запись (или как мас-
 
      сив). Элементы длинного  слова  должны  быть  заданы  таким
 
      образом, чтобы было гарантировано их непрерывное расположе-
 
      ние в памяти. Поле для первого байта или слова есть младший
 
      байт или младшее слово в длинном слове; поле для последнего
 
      слова или байта есть старший байт или старшее слово в длин-
 
      ном   слове.   Например,   сегмент    программы    передает
 
      UIC [200,210] и SYS$CREPRC. Так как  параметр  типа  RECORD
 
      нельзя передать по значению, UNION используется для  созда-


                                - 44 -
 
                          00152-01 97 09-1
 
 
      ния поля величиной в длинное слово, которое можно  передать
 
      по значению:
 
      STRUCTURE /UIC/
 
       UNION
 
        MAP
 
         INTEGER*2 MEMBER /'200'0/
 
         INTEGER*2 GROUP /'210'0/
 
        END MAP
 
        MAP
 
         INTEGER*4 NUMBER
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
      RECORD /UIC/ MY_UIC
 
      INTEGER*4 STATUS,
 
      2         SYS$CREPRC
 
      STATUS = SYS$CREPKC (,
 
      2                    'SYS$USER:[ACCOUNT]ADDEND',
 
      2                    ,,,,,,
 
      2                    %VAL(4),
 
      2                    %VAL(MY_UIC.NUMBER),,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      END
 
 
 
           1.5.5.2. Установка битов
 
 
           Если системно-определенная  процедура  требует,  чтобы
 
      длинное слово содержало маску, то определенные  биты  этого
 
      длинного слова содержат  специальную  информацию  для  этой
 
      процедуры. Для установки битов в маске используется один из
 


                                - 45 -
 
                          00152-01 97 09-1
 
 
      следующих способов:
 
           1) приравнять длинное слово  к  системно-определенному
 
      значению маски:
 
           2) установить соответствующие  биты  в  длинном  слове
 
      (встроенная процедура IBSET устанавливает бит).
 
      Как правило, для описания возможных значений маски  исполь-
 
      зуются системно-определенные  идентификаторы,  связанные  с
 
      нужным битовым смещением. Например, описание флаговых аргу-
 
      ментов для системной служебной процедуры SYS$CRMPSC  содер-
 
      жит символы, приведенные в табл.3.
 
                                                        Таблица 3
 
      -----------------------------------------------------------
                       !
            Флаг       !           Описание
                       !
      -----------------------------------------------------------
                       !
      SEC$M_GBL        !    Страницы образуют глобальную секцию.
                       !
                       ! по умолчанию секция личная
                       !
      SEC$M_CRF        !    Страницы являются копией по ссылке.
                       !
                       ! по умолчанию страницы разделяемые
                       !
      SEC$M_DZRO       !    Обнуленные страницы загружаются по
                       !
                       ! запросу. По умолчанию это не так
                       !
      SEC$M_WRT        !    Страницы образуют секцию, открытую
                       !
                       ! для чтения и записи. По умолчанию
                       !
                       ! страницы образуют секцию, открытую
                       !
                       ! только для чтения
 
 
           Вообще говоря, с каждым битом связано два набора иден-
 
      тификаторов: форма $V, представляющая смещение бита, и фор-
 
      ма $M, определяющая значение маски для этого смещения бита.
 
      Например, ссылка на смещение бита для  флагового  параметра
 
      SYS$CRMPSC использует префикс SEC$V вместо SEC$M. Идентифи-
 


                                - 46 -
 
                          00152-01 97 09-1
 
 
      каторы смещений битов определены в том  же  модуле,  что  и
 
      идентификаторы значений масок.
 
           Для создания открытой на чтение и  запись  страницы  с
 
      помощью значения маски $M используется SEC$M_WRT в качестве
 
      значения маски; все биты, кроме SEC$V_WRT,  будут  сброшены
 
      в 0. Идентификаторы SEC$ определены  в  модуле  $SECDEF  из
 
      библиотеки системных об'ектных или разделяемых  образов,  а
 
      также в библиотеке системных определений СП ФОРТРАН.
 
 
           Пример.
 
      INCLUDE '($SECDEF)'
 
      STATUS = SYS$CRMPSC (ADDR,
 
      2                  RET_ADDR,
 
      2                  ,
 
      2                  %VAL (SEC$M_WRT), !Маска
 
      2                  ,,,
 
      2                  %VAL(CHAN),
 
      2                  ,,,)
 
           Для создания страниц - копий по ссылке,  открытых  для
 
      записи и чтения, используется комбинация с помощью логичес-
 
      кого оператора .OR. Значений  SEC$M_GRF  и  SEC$M_WRT;  все
 
      биты, кроме SEC$V_WRT, сбрасываются в ноль:
 
      INCLUDE '($SECDEF)'
 
      INTEGER*4 MASK /O/
 
      MASK = SEC$M_CRF .OR. SEC$M_WRT
 
      STATUS = SYS$CRMPSC (ADDR,
 
      2                    RET_ADDR,
 
      2                    ,
 
      2                    %VAL(MASK),  !Маска
 
      2                    ,,,
 


                                - 47 -
 
                          00152-01 97 09-1
 
 
      2                    %VAL(CHAN),
 
      2                    ,,,)
 
           Для использования  значения  масок  $M  без  обнуления
 
      остальных  битов  маски  используется  логический  оператор
 
      .OR., об'единяющий существующее значение маски с новым зна-
 
      чением:
 
      INCLUDE '($SECDEF)'
 
      INTEGER MASK
 
      MASK = SEC$M_WRT
 
      !
 
      ! Продолжение программы
 
      !
 
      MASK = MASK .OR. SEC$M_CRF
 
           Для создания страниц-копий  по  ссылке,  открытых  для
 
      записи и чтения с использованием смещения битов  $V,  уста-
 
      навливаются биты SEC$V_CRF и SEC$V_WRT:
 
      INCLUDE '($SECDEF)'
 
      INTEGER MASK /O/
 
      мASK = IBSET (MASK,SEC$V_GRF)
 
      MASK = IBSET (MASK,SEC$V_WRT)
 
      STATUS = SYS$CRMS-C (ADDR,
 
      2                   RET_ADDR,
 
      2                   ,
 
      2                   %VAL(MASK), ! Маска
 
      2                   ,,,
 
      2                   %VAL(CHAN),
 
      2                    ,,,)
 


                                - 48 -
 
                          00152-01 97 09-1
 
 
           1.5.6. Передача квадрослов
 
 
           Для передачи  системно-определенной  процедуре  адреса
 
      параметра величиной в квадрослово (передача  квадрословного
 
      значения по ссылке)  задается  либо  двухэлементный  массив
 
      типа INTEGER*4, либо запись.  Элементы  квадрослова  должны
 
      быть заданы таким образом, чтобы было гарантировано их неп-
 
      рерывное размещение в памяти.
 
 
           Пример.
 
           Процедура SYS$BINTIM преобразует  абсолютное  время  в
 
      двоичное и возвращает его в виде квадрослова:
 
      ! Абсолютное время
 
      CHARACTER*(*) TIME_STR
 
      PARAMETER (TIME_STR='23-JAN-1987 14:30:22.00')
 
      ! Двоичное время
 
      INTEGER*4 TIME(2)
 
      ! Описание системной процедуры
 
      INTEGER*4 SYS$BINTIM
 
      STATUS = SYS$BINTIM (TIME_STR,
 
      2                    TIME)
 
           Системно-определенные процедуры могут требовать указа-
 
      ния битовых, байтовых,  словных  и  длиннословных  значений
 
      внутри квадрословного параметра.
 
 
 
           1.5.6.1. Задание байтов, слов и длинных слов
 
 
           Для задания внутри  квадрослова  байтовых,  словных  и
 
      длиннословных значений это квадрослово  необходимо  опреде-
 
      лить как запись. Элементы квадрослова должны быть заданы  в
 
      записи (или массиве) таким образом, чтобы было гарантирова-
 


                                - 49 -
 
                          00152-01 97 09-1
 
 
      но их непрерывное размещение в  памяти.  Поле  для  первого
 
      байта, слова или  длинного  слова  -  это  младший  элемент
 
      (байт, слово или длинное слово) квадрослова; поле для  пос-
 
      леднего байта, слова или или длинного слова -  это  старший
 
      элемент квадрослова.
 
 
           Пример.
 
           Параметр IOSB процедуры SYS$GETJPI требует  квадросло-
 
      во, в котором различаются два слова  и  следующее  за  ними
 
      длинное слово; младшее слово содержит окончательное состоя-
 
      ние, второе слово неопределено, а  длинное  слово  содержит
 
      значение 0 (%FILL - это имя псевдополя, определенное  ФОРТ-
 
      РАНОМ):
 
      ! Переменная состояния и процедура
 
      INTEGER*4 STATUS,
 
      2         SYS$GETJPI
 
      ! Блок состояния ввода/вывода для $GETJPI
 
      STRUCTURE /IOSB/
 
       INTEGER*2 STATUS,
 
      2          %FILL
 
       INTEGER*4 ZERO /0/
 
      END STRUCTURE
 
      RECORD /IOSB/ JPI_IOSB
 
      !
 
      ! Продолжение программы
 
      !
 
      STATUS = SYS$GETJPI (,,,
 
      2                    JPI_LIST,   ! Список
 
      2                    JPI_IOSB,,) ! Состояние
 
      ! Продолжение программы .
 


                                - 50 -
 
                          00152-01 97 09-1
 
 
           В другом случае параметр  IDENT  процедуры  SYS$CRMPSC
 
      должен быть квадрословом со следующими составными частями:
 
           1) длинное слово, критерий совпадения;
 
           2) слово, младший идентификатор;
 
           3) слово, старший идентификатор.
 
           Квадрослово можно описать как запись, содержащую длин-
 
      ное слово для критерия совпадения, за которым следует об'е-
 
      динение, содержащее две структуры типа мар:  длинное  слово
 
      для младшего идентификатора  и  четырехэлементный  байтовый
 
      массив для старшего идентификатора; присвоить значение кри-
 
      терию совпадения (первому полю) - CRITERION (значения  кри-
 
      териев совпадения определяются глобальными идентификаторами
 
      SEC$C_MATALL, SEC$K_MATEQU и SEC$K_MATLEQ, которые  опреде-
 
      лены в $SECDEF);  присвоить  значение  младшего  ID  первой
 
      структуре MAP второго поля,  MINOR_ID;  присвоить  значение
 
      старшего ID четвертому элементу байтового массива во второй
 
      структуре MAP  второго  поля,  MAJOR_ID(4).  Первым  должно
 
      присваиваться значение  младшего  ID,  в  противном  случае
 
      старший байт значения младшего идентификатора затрет  стар-
 
      ший идентификатор.
 
 
           Пример.
 
      ! Определение идентификационной структуры
 
      STRUCTURE /MATCH/
 
        INTEGFR*4 CRITERION
 
        UNION
 
         MAP
 
          INTEGER*4 MINOR_ID
 
         END MAP
 
         MAP
 


                                - 51 -
 
                          00152-01 97 09-1
 
 
          BYTE MAJOR_ID(4)
 
         END MAP
 
        END UNION
 
      END STRUCTURE
 
      ! Определение идентификационной записи
 
      RECORD /MATCH/ DATA_ID
 
      ! Определение идентификаторов для критериев совпадения
 
      INCLUDE '($SECDEF)'
 
      ! Переменная состояния и процедура
 
      INTEGER*4 STATUS,
 
      2         SYS$CRMPSC
 
      !
 
      ! Продолжение программы
 
      !
 
      ! Присваивание значений идентификационной записи
 
      DATA_ID.CRITERION = SEC$K_MATEQU
 
      DATA_ID.MINOR_ID = 100
 
      DATA_ID.MAJOR_ID(4) = 1
 
      ! Секция карт
 
      STATUS = SYS$CRMPSC (IN_ADDR,
 
      2                    OUT_ADDR,
 
      2                    ,
 
      2                    %VAL(SECM_GEL .OR. SEC$M_WRT),
 
      2                    'GLOBAL_SEC',
 
      2                    DATA_ID,
 
      2                    ,
 
      2                    %VAL (CHAN),
 
      2                    ,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      ! Продолжение программы .


                                - 52 -
 
                          00152-01 97 09-1
 
 
           1.5.6.2. Установка битов
 
 
           Для создания маски квадрослово определяется как двухэ-
 
      лементный массив типа INTEGER*4. Позиции битов  в  квадрос-
 
      ловной маске имеют номера от 0 до 63;  биты  позиций  от  0
 
      до 31 принадлежат младшему  длинному  слову,  биты  позиций
 
      от 32 до 63 принадлежат старшему длинному слову. Для  уста-
 
      новки бита в любом из длинных слов  используется  процедура
 
      IBSET.
 
           Например, некоторый программный  сегмент  обеспечивает
 
      пользователю привелегии DETACH и SYSNAM на все время выпол-
 
      нения образа. Задание  1  в  качестве  параметра  процедуры
 
      SYS$SETPRV активизирует привелегию, указанную в  PRIV_MASK.
 
      Деление номера позиции бита на 32 показывает,  должен  этот
 
      бит быть установлен в старшем или  младшем  длинном  слове:
 
      значение 0 указывает на младшее длинное слово, а значение 1
 
      на старшее (индексами массива являются 0 и 1).  Остаток  от
 
      деления показывет смещение бита (глобальные  идентификаторы
 
      указывающие смещение бита, определены в модуле $PRVDEF биб-
 
      лиотеки системных определений СП ФОРТРАН):
 
 
 
      ! Включить определения идентификатора привилегии
 
      INCLUDE '($PKVDEF)'
 
      ! Маска привилегии
 
      INTEGER*4 PRIV_MASK (O:1),
 
      2         ISUB,
 
      2         IPOS
 
      ! Встроенная функция
 
      INTRINSIC MOD
 
      ! Переменная состояния и системные служебные программы
 


                                - 53 -
 
                          00152-01 97 09-1
 
 
      INTEGER*4 STATUS,
 
      2         SYS$SETPRV
 
      !
 
      ! Продолжение описаний
 
      !
 
      ISUB = PRV$V_DETACH/32
 
      IPOS = MOD (PRV$V_DETACH,32)
 
      PRIV_MASK (ISUB) = IBSET (PRIV_MASK(ISUB), IPOS)
 
      ISUB = PRV$V_SYSNAM/32
 
      IPOS = MOD (PRV$V_SYSNAM,32)
                                         
      PRIV_MASK(ISUB) = IBSET (PRIV_MASK(ISUB), IPOS)
 
      STATUS = SYS$SETPRV (%VAL(1),      ! Разрешение
 
      2                    PRIV_MASK,    ! привилегий
 
      2                    ,,)
 
      !
 
      ! Продолжение программы .
 
 
 
           1.5.7. Передача структур данных переменной длины
 
 
           Для передачи в системно-определенную процедуру  струк-
 
      туры данных переменной длины используется запись. Структуры
 
      данных  пременной  длины  обычно  описываются  структурными
 
      диаграммами, за которыми следует описание каждого  элемента
 
      структуры; элементы описываются справа  налево,  начиная  с
 
      первого длинного слова структуры. В таком порядке  элементы
 
      хранятся в памяти и, следовательно, таков же порядок  полей
 
      в записи.
 
           Для передачи структуры  переменной  длины  в  качестве
 
      параметра ITMLST процедуры SYS$GETJPI необходимо следующее:
 
           1) описать структуру  записей  ITMLST.  Каждая  запись
 


                                - 54 -
 
                          00152-01 97 09-1
 
 
      содержит либо четыре поля, приведенных в  описании  ITMLST,
 
      либо одно длинное  слово,  означающее  конец  списка  кодов
 
      об'ектов:
 
      ! Определить структуру ITMLST
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN
 
         INTEGER*2 CODE
 
         INTEGER*4 BUFADR
 
         INTEGER*4 RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
           Блок UNION внутри блока STRUCTURE может содержать один
 
      или более блоков MAP. Если внутри  одного  блока  MAP  поля
 
      определены, то поля в других блоках MAP будут  неопределен-
 
      ными;
 
           2) описать переменную ITMLST, решив, какая  информация
 
      потребуется; описать массив RECORD,  содержащий  по  одному
 
      элементу на каждый код об'екта, плюс дополнительный элемент
 
      для об'екта, оканчивающего список. Например, оператор  опи-
 
      сания массива типа RECORD по имени ITMLST  для  кодов  двух
 
      об'ектов имеет вид:
 
       RECORD /ITMLST/ JPI_LIST (3)
 
           3) описать буферы для  информации.  Для  каждого  кода
 
      об'екта определить две переменные в качестве буферов: буфер


                                - 55 -
 
                          00152-01 97 09-1
 
 
      для информации, возвращаемой SYS$GETJPI, и буфер для  длины
 
      возвращаемой информации,  выраженной  в  байтах.  Следующий
 
      сегмент  программы  описывает  буферы  для  кодов  об'ектов
 
      JPI$_PRCNAM (имя процесса) и JPI$_PRI (приоритет процесса):
 
      ! Описать буфера для информации
 
      CHARACTER*15 NAME
 
      INTEGER*4    PRIORITY
 
      INTEGER*2    NAME_LEN,
 
      2            PRIORITY_LEN
 
           4) присвоить значения полям. Для каждого кода  об'екта
 
      присвоить значения соответствующим полям элемента  массива.
 
      Первому  элементу  JPI_LIST,  что  отвечает  коду   об'екта
 
      JPI$_PRCNAM, присваиваются следующие значения:
 
           - в поле BUFLEN заносится длина в байтах буфера запра-
 
      шиваемой информации:
 
      JPI_LIST(1).BUFLEN=15
 
           - в поле CODE заносится значение  кода  об'екта  (коды
 
      запроса для SYS$GETJPI определены в модуле $JPIDEF из  биб-
 
      лиотеки системных об'ектных или разделяемых образов, а так-
 
      же из библиотеки системных определений СП ФОРТРАН:
 
      ! Определения кодов об'ектов из SYS$GETJPI
 
      INCLUDE '($JPIDEF)'
 
      JPI_LIST(1).CODE = JPI$_PRCNAM
 
           - в поле BUFADR заносится адрес буфера  для  требуемой
 
      информации (встроенная функция %LOC возвращает адрес задан-
 
      ной переменной):
 
      JPI_LIST(1).BUFADR=%LOC(NAME)
 
           - в поле RETLENADR заносится адрес  буфера  для  длины
 
      возвращаемой информации (встроенная функция %LOC возвращает
 
      адрес заданной переменной):


                                - 56 -
 
                          00152-01 97 09-1
 
 
      JPI_LIST(I).RETLENADR = %LOC(NAME_LEN)
 
           5) закрепить список кодов  об'ектов  -  полю  END_LIST
 
      последнего элемента массива присвоить значение 0:
 
           JPI_LIST(3).END_LIST = 0
 
 
           Пример.
 
           Программа GETJPI вызывает SYS$GETJPI для печати  имени
 
      (JPI$_PRCNAM) и приоретета (JPI$_PRI) текущего процесса:
 
      PROGRAM GETJPI
 
      ! Вывод имени и приоритета текущего процесса на дисплей
 
      ! Включить коды запроса
 
      INCLUDE '($JPIDEF)'
 
      ! Определить структуру ITMLST
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN
 
         INTEGER*2 CODE
 
         INTEGER*4 BUFADR
 
         INTEGER*4 RETLENADR
 
        END MAP
 
        MAP
 
         INTEGER*4 END_LIST
 
        END MAP
 
        END UNION
 
      ! Описать GETJPI ITMLST
 
      RECORD /ITMLST/ JPI_LIST(3)
 
      ! Описать буферы под информацию
 
      CHARACTER*15    NAME
 
      INTEGER*4        PRIORITY
 


                                - 57 -
 
                          00152-01 97 09-1
 
 
      INTEGER*4        NAME_LEN,
 
      2                PRIORITY_LEN
 
      ! Описать состояние и подпрограмму
 
      INTEGER*4        STATUS,
 
      2                SYS$GETJPI
 
      ! SET UP ITMLST
 
      JPI_LIST(1).BUFLEN =    15
 
      JPI_LIST(1).CODE =      %LOC(JPI$_PRCNAM)
 
      JPI_LIST(1).BUFADR =    %LOC(NAME)
 
      JPI_LIST(1).RETLENADR = %LOC(NAME_LEN)
 
      JPI_LIST(2).BUFLEN =    4
 
      JPI_LIST(2).CODE =      %LOC(JPI$_PRI)
 
      JPI_LIST(2)BUFARD=      %LOC(PRIORITY)
 
      JPI_LIST(2)RETLENADR=   %LOC(PRIORETY_LEN
 
      JPI_LIST(3) END_LIST=   O
 
      ! Вызов SYS$GETJPI
 
      STATUS = SYS$GETJPI (,,,JPI_LIST,,,)
 
      IF (.NOT.STATUS) CALL LIB$SICNAL(%VAL(STATUS))
 
      ! Вывести информацию
 
      TYPE *,'имя: ' ,NAME(1:NAME_LEN)
 
      TYPE *,'приоритет: ' ,PRIORITY
 
      END     ! Конец программы  .
 
 
 
           1.6. Чтение          информации,          возвращенной
 
      системно-определенной процедурой
 
 
           Обычно  для   использования   возвращенной   системно-
 
      определенной процедурой информации обращаются к фактическо-
 
      му параметру, содержащему эту  информацию  после  окончания
 
      выполнения системно-определенной  процедуры.  Однако,  если
 


                                - 58 -
 
                          00152-01 97 09-1
 
 
      процедура возвращает информацию в виде  маски,  адреса  или
 
      буфера, содержащего различные фрагменты информации, то надо
 
      сначала извлечь эту информацию по фактическому параметру.
 
 
 
           1.6.1. Маски
 
 
           Для  извлечения  информации   по   маске   применяется
 
      встроенная функция BTEST, проверяющая, установлен  или  нет
 
      некоторый конкретный  бит.  Например,  системная  служебная
 
      процедура SYS$READEF используется для чтения кластера  фла-
 
      гов локальных событий, а затем определяется, установлены ли
 
      флаги 2, 3 и 4.  Поскольку  системной  служебной  процедуре
 
      передается флаг 2 (FLAG2), состояние возврата  используется
 
      для выяснения того, установлен ли этот флаг:
 
 
      INCLUDE '($SSDEF)' ! Определяем символические имена
 
      INTEGER*4 FLAG2,
 
      2         FLAG3,
 
      2         FLAG4,
 
      2         CLUSTER
 
      PARAMETR (FLAG2= 2,
 
      2         FLAG3= 3,
 
      2         FLAG4= 4)
 
      ! Анализ кластера флагов локальных событий
 
      STATUS = SYS$READEF (FLAG2,
 
      2                    CLUSTER)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF (STATUS .EQ. SS$_WASSET) THEN
 
      !
 
      ! Установка флага FLAG2
 
      !


                                - 59 -
 
                          00152-01 97 09-1
 
 
      IF (BTEST (CLUSTER,FLAG3)) THEN
 
      !
 
      ! Установка флага FLAG3
 
      !
 
      IF (BTEST (CLUSTER,FLAG4)) THEN
 
      !
 
      ! Установка флага FLAG4 .
 
 
 
           Встроенная функция BTEST, в отличие от других встроен-
 
      ных битовых функций, работает  не  только  со  словами  или
 
      длинными словами. Например, для выяснения  вопроса  о  том,
 
      есть ли у процесса привилегия SETPRV,  анализируется  маска
 
      привелегий этого процесса:
 
 
 
      INCLUDE '($JPIDEF)'  ! Определяет коды запроса JPIS_
 
      INCLUDE '($PRVDEF)'  ! Определяет коды привилегии PRVS_
 
      INCLUDE '($SSDEF)'   ! Определяет коды возврата SS$_
 
      INTEGER*4 PRIV_MASK (2)
 
      ! Описать системную процедуру
 
      INTEGER*4 LIB$GETJPT
 
      STATUS = LIB$GETJPI (JPI$_CURPRIV,
 
      2                    ,,
 
      2                    PRIV_MASK,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Сигнализировать, если SETPRV не задействована
 
      IF (.NOT. (BTEST (PRIV_MASK(1),PRV$V_SETPRV)) )
 


                                - 60 -
 
                          00152-01 97 09-1
 
 
           1.6.2. Адреса
 
 
           Язык ФОРТРАН различает байтовые, символьные,  числовые
 
      и логические данные. В этом  языке  не  существует  понятия
 
      адреса данных. Системно-ориентированные процедуры не  напи-
 
      саны исключительно для ФОРТРАНа и некоторые из них  возвра-
 
      щают не сами данные, а их адреса. Способ  доступа  к  таким
 
      данным зависит от того, являются ли они числовыми или  сим-
 
      вольными.
 
 
 
           1.6.2.1. Числовые данные
 
 
           Чтобы извлечь числовые данные, находящиеся по  извест-
 
      ному адресу, необходимо выполнить следующее:
 
           1) использовать механизм передачи параметра по  значе-
 
      нию при передаче адреса в какую-либо подпрограмму;
 
           2) в этой подпрограмме  описать  формальный  параметр,
 
      связанный с адресом, как числовое значение требуемого  раз-
 
      мера (двойное слово);
 
           3) для получения доступа к данным обратиться  к  этому
 
      формальному параметру по ссылке.
 
 
 
           1.6.2.2. Символьные данные
 
 
           Для получения доступа к символьным данным по известно-
 
      му адресу необходимо:
 
           1) создать дескриптор из  двух  длинных  слов,  указав
 
      число символов в первом слове и адрес данных - во втором;
 
           2) передать дескриптор в нужную подпрограмму по  ссыл-
 
      ке, что принято в СП ФОРТРАН по умолчанию;
 
           3) формальный параметр, связанный с этим дескриптором,
 


                                - 61 -
 
                          00152-01 97 09-1
 
 
      описать в подпрограмме  как  символьную  переменную  нужной
 
      длины или строку символов передаваемой длины;
 
           4) для получения доступа к данным обратиться  к  этому
 
      формальному параметру.
 
 
           Пример.
 
           Программа SORTING сортирует файл  и  затем  с  помощью
 
      программ SOR$STAT определяет,  какая  версия  обслуживающей
 
      программы сортировки была использована. SOR$STAT возвращает
 
      адрес  строки  со  счетчиком,  содержащей   номер   версии,
 
      SORTING.FOR передает этот адрес в подпрограмму GET_VERSION,
 
      а GET_VERSION имеет доступ к строке со счетчиком,  содержа-
 
      щей номер версии (первый байт строки символов со  счетчиком
 
      содержит число символов в строке):
 
      ! Программа SORTING:
 
      CHARACTER*256 FILENAME_IN,
 
      2             FILENAME_OUT
 
      INTEGER*4     FN_SIZE_IN,
 
      2             FN_SIZE_OUT
 
      ! Получить номер версии
 
      INTEGER*4     IDENT,
 
      2             VERSION(2)
 
      CHARACTER*1O  V_STRING
 
      EXSTERNAL     SOR$K_IDENT
 
      ! Ключевая информация SORT
 
      INTEGER*2 KEY_BUFFER(5)
 
      !
 
      ! Продолжение описания
 
      !
 
      ! Описать состояние и системные программы
 


                                - 62 -
 
                          00152-01 97 09-1
 
 
      INTEGER STATUS,
 
      2       SOR$INIT_SORT,
 
      2       SOR$PASS_FILES,
 
      2       SOR$BEGIN_SORT,
 
      2       SOR$SORT_MERGE,
 
      2       SOR$END_SORT,
 
      2       SOR$STAT
 
      !
 
      ! Выполняемые операторы
 
      !
 
      ! Передать файлы в SORT
 
      STATUS = SOR$PASS_FILES (FILENAME_IN (1:FN_CIZE_IN),
 
      2                        FILENAME_OUT(1:FN_SIZE_OUT))
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! Задать программе SORT ключевую информацию
 
      STATUS = SOR$BEGINE_SORT (KEY_BUFFER)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! Начало сортировки
 
      STATUS = SOR$SORT_ MERGE ()
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      ! Взять версию SORT
 
      STATUS = SOR$STAT (%LOC(COR$K_IDENT),
 
      2                  IDENT)
 
      IF (.NOT. STATUS) CALL LIB%SIGNAL (%VAL (STATUS)
 
      ! Создяать дескриптор и передать его в GET_VERSION
 
      VERSION(1) = 12
 
      VERSION(2) = IDENT
 
      CALL GET_VERSION (VERSION,
 
      2                 V_STRING)
 
      ! Конец программы SORT


                                - 63 -
 
                          00152-01 97 09-1
 
 
      STATUS = SOR$END_SORT(1)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !
 
      ! Продолжение программы
 
      !
 
      ! Подпрограмма GET_VERSION:
 
      SUBROUTINE GET_VERSION (VERSION,
 
      2                       V_STRING)
 
      ! Формальные параметры:
 
      ! Строка VERSION
 
      CHARACTER*(*) VERSION,
 
                    V_STRING
 
      ! Длина строки VERSION
 
      INTEGER*4 LENGTH
 
      ! Встроенная процедура
 
      INTRINSIC ICHAR
 
      LENGTH = ICHAR (VERSION(1:1))
 
      V_STRING = 'отсортирован с использованием версии'
 
      2       //VERSION (2:LENGTH)//'программы SORT'
 
      END     ! Конец подпрограммы   .
 
 
 
           1.6.3. Буферы
 
 
           В том случае, когда несколько порций информации  возв-
 
      ращаются в одном общем буфере, нужно определить  переменную
 
      буфера как запись; каждое поле в этой записи должно  содер-
 
      жать одну порцию информации.
 
           Системная служебная процедура SYS$CREPRC создает  про-
 
      цесс и, возможно, записывает сообщение в почтовый ящик  при
 
      уничтожении процесса. Это сообщение об уничтожении процесса
 


                                - 64 -
 
                          00152-01 97 09-1
 
 
      представляет собой буфер, в  котором  содержатся  различные
 
      части информации об уничтоженном процессе. Чтобы  прочитать
 
      это сообщение, нужно определить  переменную  для  сообщения
 
      записью, в которой отведены отдельные поля для каждой части
 
      информации. Так как нужно  читать  сообщение  из  почтового
 
      ящика, используя форматный ввод,  то  проще  всего  создать
 
      запись, альтернативно определенную как строка символов  или
 
      последовательность полей. Сообщение можно прочитать в  поле
 
      строки символов, а остальные поля использовать  для  ссылки
 
      на эту информацию.
 
           Пример.
 
      ! Описание структуры буфера:
 
      SтRUCTURE /TERM_MESSAGE/
 
       UNION
 
        MAP
 
         INTEGER*2      MSGTTYP,
 
      2                 MSGSIZ
 
         INTEGER*4      FINALSTS
 
      2                 PID,
 
      2                 JOBID,
 
      2                 TERMTIME(2)
 
         CHARACTER*8    ACCOUNT
 
         CHARACTER*12   USERNAME
 
         INTEGER*4      CPUTIM,
 
      2                 PAGEFLST,
 
      2                 PFGLPEAK,
 
      2                 WSPEAK,
 
      2                 BIOGNT,
 
      2                 DIOCNT,
 
      2                 VOLUMES,


                                - 65 -
 
                          00152-01 97 09-1
 
 
      2                 LOGIN(2),
 
      2                 OWNER       
 
        END MAP
 
        MAP
 
         CHARACTER*84 MESSAGE
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
 
           Текст программы создания процесса, чтения сообщения  о
 
      прекращении при уничтожении  созданого  процесса  и  вывода
 
      строки информации об уничтоженном процессе:
 
 
      ! Переменная состояния
 
      INTEGER STATUS,
 
      2       SYS$CREMBX,
 
      2       SYS$GETDVIW,
 
      2       SYS$CREPEC,
 
      2       SYS$ASCTIM
 
      ! Определить буфер сообщения о прекращении
 
      STRUCTURE /TERM_MESSAGE/
 
       UNION
 
        MAP
 
         INTEGER*2     MSGTYP,
 
      2                MSGSIZ,
 
         INTEGER*4     FINALSTS,
 
      2                PID,
 
      2                JOBID,
 
      2                TERMTIME(2)
 
         CHARACTER*8   ACCOUNT
 
         CHARACTER*12  USERNAME


                                - 66 -
 
                          00152-01 97 09-1
 
 
         INTEGER*4     CPUTIM,
 
      2                PAGEFLST,
 
      2                PFGLPEAK,
 
      2                WSPEAK,
 
      2                BIOCNT,
 
      2                DIOCNT,
 
      2                VOLUMES,
 
      2                LOGIN(2),
 
      2                OWNER
 
        END MAP
 
        MAP
 
         CHARACTER*84 MESSAGE
 
        END MAP
 
       END UNION
 
      END STRUCTURE
 
      RECORD /TERM_MESSAGE/ TERMINATION
 
      ! Строка для времени прекращения
 
      CHARACTER*24 TERM_STRING
 
      INTEGER*4    TERM_LEN
 
      ! Описать ITMLST для $GETDVI
 
      STRUCTURE /ITMLST/
 
       UNION
 
        MAP
 
         INTEGER*2 BUFLEN
 
         INTEGER*2 CODE
 
         INTEGER*4 BUFADR
 
         INTEGER*4 RETLENADR
 
       END MAP
 
       MAP
 
        INTEGER*4 END_LIST


                                - 67 -
 
                          00152-01 97 09-1
 
 
       END MAP
 
       END UNION
 
      END STRUCTURE
 
      RECORD /ITMLST/ DVI_LIST(2)
 
      ! Буферы для $GETDVI
 
      INTEGER*4 UNIT,
 
      2          UNIT_LEN
 
      ! Код об'екта для $GETDVI
 
      EXTERNAL DVI$_UNIT
 
      ! Канал для почтового ящика прекращения
 
      INTEGER*2 MBX_CHAN
 
      ! Создать почтовый ящик
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,
 
      2                    ,,,,
 
      2                    'MBX_NAME')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Принять номер устройства почтового ящика
 
      ! Установить для $GETDVI
 
      DVI_LIST(1).BUFLEN = 4
 
      DVI_LIST(1).CODE = %LOC(DVI$_UNIT)
 
      DVI_LIST(1).BUFADR = %LOC(UNIT)
 
      DVI_LIST(1).RETLENADR = %LOC(UNIT_LEN)
 
      DVT_LIST(1).END_LIST = O
 
      ! Вызов $GETDVI
 
      STATUS = SYS$GETDVIW (,
 
      2                     %VAL(MBX_CHAN),
 
      2                     ,
 
      2                     DVI_LIST,
 
      2                     ,,,)


                                - 68 -
 
                          00152-01 97 09-1
 
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Создать процесс, передав номер устройства
 
      ! Почтового ящика
 
      STATUS = SYS$GREPRC(,
 
      2        'SYS$USER:[ACCOUNT]ADDEND',
 
      2        ,,,,,,
 
      2        %VAL(4),,
 
      2        %VAL(UNIT),)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Прочесть сообщение о прекращении из почтового ящика
 
      OPEN (UNIT = 2,
 
      2     FILE = 'MBX_NAME',
 
      2     CARRIAGECONTROL = 'LIST',
 
      2     STATUS = 'NEW')
 
      READ  (UNIT=2,
 
      2     FMT = '(A)') TERMINATION.MESSAGE
 
      ! Преобразовать время прекращения в код КОИ-8
 
      STATUS = SYS$ASCTIM (TERM_LEN,
 
      2                    TERM_STRING,
 
      2                    TERMINATION.TERMTIME,)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Вывести сообщение о прекращении
 
      TYPE *,'процесс ',TERMINATION.PID,' завершился ',
 
      2      TERM_STRING(1:TERM_LEN),'.'
 
      TYPE *,'код завершения ',TERMINATION.FINALSTS,'.'
 
      END     ! Конец программы   .
 
 
 
 
 
 
 
 


                                - 69 -
 
                          00152-01 97 09-1
 
 
 
 
 
 
 
                  2. Организация программной единицы
 
 
 
           Программная единица представляет собой  либо  головную
 
      программу, либо подпрограмму. Хранение  каждой  программной
 
      единицы или группы родственных программных  единиц  в  виде
 
      отдельных файлов способствует облегчению организации  слож-
 
      ных программ.
 
           Общая схема строения программной единицы  представлена
 
      на рис. 2.
 
       -------------------------------------------
       !PROGRAM имя      \                       !
       !                  \                      !
       !SUBROUTINE имя   --> - Одно на выбор     !
       !                  /                      !
       !Тип FUNCTION имя /                       !
       !                                         !
       !                                         !
       !      -----------------------            !
       !      !                     !            !
       !      ! Операторы описаний  !            !
       !      !                     !            !
       !      -----------------------            !
       !                                         !
       !      -----------------------            !
       !      !                     !            !
       !      !Выполняемые операторы!            !
       !      !                     !            !
       !      -----------------------            !
       !                                         !
       !                                         !
       !END                                      !
       !                                         !
       -------------------------------------------
 
 
                                Рис. 2
 
 
           Первым оператором программной единицы является  опера-
 
      тор PROGRAM (не обязателен, но  рекомендуется),  SUBROUTINE
 
      или FUNCTION, идентифицирующий программную единицу по имени
 
      и (для функций) по типу.  Имя  программной  единицы  должно


                                - 70 -
 
                          00152-01 97 09-1
 
 
      быть уникальным среди имен других программных единиц и бло-
 
      ков общей памяти, используемых внутри одной программы.
 
           Вслед за первым оператором  идут  операторы  описаний,
 
      которые в основном идентифицируют элементы данных и опреде-
 
      ляют, как они  могут  использоваться.  Следующая  группа  -
 
      выполняемые операторы, которые фактически и выполняют рабо-
 
      ту программной единицы. Первый  выполняемый  в  программной
 
      единице оператор называется входной точкой этой программной
 
      единицы. Последним оператором  программной  единицы  должен
 
      быть оператор END.
 
 
           Пример.
 
           Оформление исходного текста процедуры CALC_SUMS,  хра-
 
      нящегося в виде файла CALC_SUMS.FOR:
 
      !****************
 
      !Оператор функции
 
      !****************
 
      INTEGER FUNCTION CALC_SUMS (TOTAL_HOUSES,
 
      2                           PERSONS_HOUSE,
 
      2                           ADULTS_HOUSE,
 
      2                           INCOME_HOUSE,
 
      2                           AVG_PERSONS_HOUSE,
 
      2                           AVG_ADULTS_HOUSE,
 
      2                           AVG_INCOME_NOUSE,
 
      2                           AVG_INCOME_PERSON,
 
      2                           MED_INCOME_HOUSE)
 
      !Функция для вычисления средних и медианы
 
      ! ****************
 
      !Операторы описаний
 
      ! ****************
 


                                - 71 -
 
                          00152-01 97 09-1
 
 
      !Описание формальных параметров
 
      INTEGER TOTAL_HOUSES.
 
      2         MED_INCOME_HOUSE
 
      REAL PERSONS_HOUSE (2048),
 
      2    ADULTS_HOUSE (2048),
 
      2    INCOME_HOUSE (2048),
 
      2    AVG_PERSONS_HOUSE,
 
      2    AVG_ADULTS_HOUSE,
 
      2    AVG_INCOME_HOUSE,
 
      2    AVG_INCOME_PERSON
 
      ! Описание управляющих переменных
 
      REAL PERSONS,
 
      2    ADULTS,
 
      2    INCOME
 
      INTEGER MEDIAN (101)
 
      ! Описание переменных и значений состояния
 
      INTEGER STATUS_OK
 
      PARAMETER (STATUS_OK=1)
 
 
 
      !  ****************
 
      !Выполняемые операторы
 
      !  ****************
 
      ! Вычисление итогов
 
      DO I = 1, TOTAL HOUSES
 
         PERSONS = PERSONS + PERSONS_HOUSE (I)
 
         ADULTS = ADULTS + ADULTS_HOUSE (I)
 
         INCOME = INCOME + INCOME_HOUSE (I)
 
      ! Деление и усечение до целого
 
         J = INCOME_HOUSE (I) / 1000
 
      ! Доходы ниже $1000 приравниваются к $1000


                                - 72 -
 
                          00152-01 97 09-1
 
 
        IF (J .EQ. 0) J = 1
 
      ! Доходы свыше $100000 не различаются
 
        IF (J .GT.  101) J = 101
 
      ! Подсчет доходов в той группе
 
        MEDIAN (J) = MEDIAN (J) + 1
 
      END DO
 
      ! Подсчет средних
 
      AVG_PERSONS_HOUSE = PERSONS/TOTAL_HOUSES
 
      AVG_ADULTS_HOUSE = ADULTS/TOTAL_HOUSES
 
      AVG_INCOME_HOUSE = INCOME/TOTAL_HOUSES
 
      AVG_INCOME_PERSON = INCOME/PERSONS
 
      ! Подсчет медианы дохода на дом
 
      J = 1
 
      DO I=2, 101
 
        IF (MEDIAN (I) .GT. MEDIAN (J)) J = I
 
      END DO
 
      MED_INCOME_HOUSE = J * 1000
 
      ! Возврат
 
      CALC_SUMS = STATUS_OK
 
      !************
 
      !Оператор END
 
      !************
 
      END ! Конец подпрограммы .
 
 
 
           2.1. Локальная память
 
 
           Под локальной памятью понимают место,  отведенное  для
 
      тех данных, которые доступны только внутри определяющей  их
 
      программной единицы. Этот  термин  не  относится  к  месту,
 
      отведенному под данные,  доступные  из  других  программных
 


                                - 73 -
 
                          00152-01 97 09-1
 
 
      единиц или определенные в них. Например, данные в операторе
 
      INTEGER используют локальную память, а данные  в  операторе
 
      COMMON - глобальную.
 
 
 
           2.1.1. Определение типа данных
 
 
           Определить тип данных можно одним из двух способов:
 
           1) явно - определить тип данных переменной в операторе
 
      описания типа;
 
           2) неявно  -  тип  переменной  определяется  путем  ее
 
      использования в выполняемом операторе без упоминания  имени
 
      переменной в операторе описания типа. По умолчанию  ФОРТРАН
 
      неявно определяет переменные, чьи имена начинаются  с  букв
 
      I, J, K, L, M и N как целые, а все остальные - как перемен-
 
      ные типа REAL*4, т.е. вещественные.
 
           Например,   сегмент    программы    явно    определяет
 
      TOTAL_HOUSES как переменную типа  INTEGER*4,  явно  опреде-
 
      ляет 6 переменных в операторе  REAL*4  как  вещественные  и
 
      неявно определяет I как переменную типа INTEGER:
 
 
      !Описание переменных для хранения статистики
 
      INTEGER*4 TOTAL_ROUSES     !Количество домохозяйств
 
      REAL*4 PERSONS_HOUSE(2048) !Количество людей на
 
                                  домохозяйство
 
      2      ADULTS_HOUSE(2048), !Количество взрослых в
 
                                 !домохозяйстве
 
      2      INCOME_HOUSE(2048), !Валовой доход на домохозяйство
 
      2      PERSONS,            !Всего людей
 
      2      ADULTS,             !Всего взрослых
 
      2      INCOME              !Общий валовой доход
 
      !Вычисление итогов


                                - 74 -
 
                          00152-01 97 09-1
 
 
      DO I = 1, TOTAL_HOUSES
 
        PERSONS = PERSONS + PERSONS_HOUSE(I)
 
        ADULTS = ADLTS_HOUSE(I)
 
        INCOME = INCOME_HOUSE(I)
 
      END DO  ! Конец цикла .
 
 
           Для отмены действующих по умолчанию правил определения
 
      типа применяется оператор INPLICIT. Например, сегмент прог-
 
      раммы неявно определяет все переменные, начинающиеся с букв
 
      A, P и I как REAL*4 и явно описывает I как INTEGER*4. Явное
 
      определение типа отменяет неявное (этот пример функциональ-
 
      но эквивалентен предыдущему):
 
      !Неявное определение типов
 
      IMPLICIT REAL*4 (A,P,I)
 
      !Управляющая переменная цикла
 
      INTEGER*4 I
 
      !Вычисление итогов
 
      DO I=1, TOTAL_HOUSES
 
        PERSONS = PERSONS + PERSONS_HOUSE (I)
 
        ADULTS = ADULTS_HOUSE (I)
 
        INCOME = INCOME_HOUSE (I)
 
      END DO  ! Конец цикла   .
 
 
           Для отмены всех неявных определений типов используется
 
      оператор IMPLICIT NONE. Если задан оператор IMPLICIT  NONE,
 
      то другие операторы IMPLICIT задавать нельзя, а  все  пере-
 
      менные должны быть описаны явно.  В  программах,  требующих
 
      усилий при их создании, рекомендуется использовать оператор
 
      IMPLICIT NONE (для облегчения поиска  неверно  определенных
 
      или используемых переменных).
 
           Операторы IMPLICIT или оператор IMPLICIT  NONE  должны


                                - 75 -
 
                          00152-01 97 09-1
 
 
      предшествовать операторам описания данных. Неявное  опреде-
 
      ление типов данных не затрагивает встроенные функции  языка
 
      ФОРТРАН, но влияет на тип любой другой внешней функции.
 
 
 
           2.1.2. Инициализация переменной
 
 
           Переменная может быть инициализирована во время компи-
 
      ляции (до вызова программной единицы) или во время выполне-
 
      ния (при каждом вызове программной единицы).
 
           Переменная инициализируется во время компиляции,  если
 
      достаточно, чтобы начальное значение ей присваивалось толь-
 
      ко один раз. Переменные, инициализируемые во время компиля-
 
      ции, требуют места в файле образа, поэтому для больших мас-
 
      сивов необходимо предусмотреть возможность их инициализации
 
      во время выполнения.
 
           Переменная инициализируется во время выполнения,  если
 
      при каждом новом выполнении данной программной единицы нуж-
 
      на повторная инициализация этой переменной.
 
           Всем переменным, начальные значения  которых  не  были
 
      указаны явно,  присваиваются  нулевые  начальные  значения.
 
      Однако в серьезных программах не следует на это полагаться,
 
      поскольку в последующих версиях языка ФОРТРАН  вместо  этих
 
      нулевых значений может появиться что-нибудь другое.
 
 
 
           2.1.2.1. Инициализация во время компиляции
 
 
           Для задания переменной начального значения в  точности
 
      один раз применяется оператор DATA. Оператор DATA  присваи-
 
      вает переменной значение во время  компиляции.  При  первом
 
      выполнении программная единица содержит в  указанной  пере-
 
      менной значение, заданное оператором DATA. При  последующих


                                - 76 -
 
                          00152-01 97 09-1
 
 
      выполнениях той же  самой  программной  единицы  переменная
 
      отразит все изменения, происшедшие с начальным значением.
 
 
           Пример.
 
           В операторе DATA задаются начальные  значения  массива
 
      ACCTS, целой переменной PAGE_COUNT и символьной  переменной
 
      PAGE_HEADER:
 
 
      !Описание переменных
 
      INTEGER PAGE_COUNT,
 
      2       ACCTS (10)
 
      CHARACTER*55 PAGE_HEADER
 
      !Инициализация переменных
 
      DATA PAGE_COUNT /1/
 
      DATA ACCTS /10*5/
 
      DATA PAGE_READER /'HOUSE NO
 
      2    PERSONS HOUSE ADULTE-HOUSE INCOME-HOUSE'/
 
 
           Первый оператор DATA присваивает значение 1 переменной
 
      PAGE_COUNT, второй - значение 5 каждому из десяти элементов
 
      массива ACCTS, третий - строку символов, содержащую 55 сим-
 
      волов - переменной PAGE_HEADER.
 
           Можно инициализировать часть  массива,  задав  неявный
 
      цикл внутри оператора DATA:
 
       INTEGER ACCTS (5)
 
       DATA (ACCTS(I),I = 1,4) /4*2/  .
 
 
           Можно также инициализировать переменную непосредствен-
 
      но в операторе описания типа, задав начальное значение сра-
 
      зу вслед за переменной, заключив его  в  две  косые  черты.
 
      Инициализируется только переменная,  непосредственно  пред-
 
      шествующая значению.  Чтобы  таким  путем  инициализировать


                                - 77 -
 
                          00152-01 97 09-1
 
 
      массив, необходимо задать значение каждого элемента  масси-
 
      ва. Значения, заданные в операторе  описания  типа,  как  и
 
      значения, заданные в операторе DATA, присваиваются перемен-
 
      ным на этапе компиляции. Например, для инициализации  пере-
 
      менной ELEMENTS  значением  2,  а  двух  элементов  массива
 
      NAME - значениями JACK и JOB (NEW_NAME  не  инициализирует-
 
      ся), можно использовать операторы:
 
      INTEGER*4 ELEMENTS /2/
 
      CHARACTER*10 NEW_NAME,
 
      2            NAMES(2) /'JACK', 'JOB'/
 
 
 
           2.1.2.2. Инициализация во время выполнения
 
 
           Если при  выполнении  программы  какая-то  программная
 
      единица вызывается более одного раза, то ее переменные  (за
 
      исключением формальных параметров) не инициализируется пов-
 
      торно, то есть сохраняют значения, оставшиеся после  преды-
 
      дущего вызова этой программной единицы.  Для  инициализации
 
      переменной при каждом вызове подпрограммы,  содержащей  эту
 
      переменную, используются операторы присваивания или ввода в
 
      выполняемой части программы.
 
 
           Пример.
 
           Переменная J получает значение 1 с  помощью  оперетора
 
      присваивания:
 
      INTEGER*4 MEDIAN_INCOME_HOUSE,
 
      2      MEDIAN (101)
 
      !Вычисление медианы дохода на дом
 
      J = 1
 
      DO I=2, 101
 
      IF (MEDIAN(I) .GT. MEDIAN(J)) J = I


                                - 78 -
 
                          00152-01 97 09-1
 
 
      END DO
 
      MED_INCOME_HOUSE = J*1000
 
 
 
           2.1.3. Именованные константы
 
 
           Постоянным значениям (константам) можно присвоить сим-
 
      волические имена с помощью оператора PARAMETER в описатель-
 
      ной  части  программной  единицы.  В  программной  единице,
 
      содержащей оператор PARAMETER, вместо константы можно всюду
 
      применять ее символическое имя. Оператор PARAMETER особенно
 
      полезен для именования констант, смысл которых  может  ока-
 
      заться неизвестен читающему программу.  Например,  оператор
 
      PARAMETER присваивает целой константе 1  символическое  имя
 
      STATUS_OK:
 
       INTEGER STATUS_OK
 
       PARAMETER (STATUS_OK=1)
 
      а  другой  оператор  PARAMETER   дает   символическое   имя
 
      PAGE_HEAD ER заданной строке символов:
 
      CHARACTER*(*) PAGE_HEADER
 
      PARAMETER(PAGE_HEADER='HOUSE_NO,
 
      2       PERSONS_HоUSE ADUTS_HOUSE INCOME_HOUSE')
 
           Заметим, что для определения  длины  строки  символов,
 
      используемой в операторе PARAMETER,  можно  воспользоваться
 
      обозначением передаваемой длины, CHARACTER*(*).  Переменная
 
      PEGE_HEADER типа CHARACTER принимает длину символьной конс-
 
      танты, указанной в операторе PARAMETER.
 


                                - 79 -
 
                          00152-01 97 09-1
 
 
           2.1.4. Эквивалентные переменные
 
 
           Эквивалентность переменных позволяет по двум  и  более
 
      именам ссылаться на одну и ту же область  памяти.  Оператор
 
      EQUIVALENCE устанавливает эквивалентность переменных, нахо-
 
      дящихся в одной программной единице. Эквивалентность  пере-
 
      менных  из  разных  программных  единиц  устанавливается  с
 
      помощью оператора COMMON.
 
           Оператор EQUIVALENCE гарантирует,  что  переменным  из
 
      заключенного в скобки списка отводится память, начинающаяся
 
      с одного и того же адреса. Например,  оператор  EQUIVALENCE
 
      связывает переменные UIC  и  UIC_LOAD,  после  чего  массив
 
      UIC_LOAD типа INTEGER*2 ссылается на то же место в  памяти,
 
      что и переменная UIC типа INTEGER*4:
 
      INTEGER*2 UIC_LOAD(2)
 
      INTEGER*4 UIC
 
      EQUIVALENCE (UIC,UIC_LOAD)
 
           Эквивалентность может быть установлена  между  данными
 
      различных типов, а также между элементами различных  масси-
 
      вов. При установлении эквивалентности между элементами раз-
 
      личных массивов остальные элементы этих массивов также ста-
 
      нут эквивалентными. Можно сделать эквивалентными переменную
 
      из блока общей памяти  и  переменную,  не  принадлежащую  к
 
      общей памяти, но нельзя устанавливать эквивалентность между
 
      двумя переменными из блока общей памяти. Нельзя  устанавли-
 
      вать эквивалентность между формальными параметрами и  любым
 
      другим аргументом.
 
           Если устанавливается эквивалентность между двумя  мас-
 
      сивами, более короткий из которых принадлежит к блоку общей
 
      памяти, то блок общей памяти автоматически  будет  увеличен
 


                                - 80 -
 
                          00152-01 97 09-1
 
 
      таким образом, что будет включать в себя все элементы более
 
      длинного массива. Однако, так  как  начальный  адрес  блока
 
      общей памяти не  может  изменяться,  то  нельзя  установить
 
      эквивалентность между массивами в  том  случае,  когда  это
 
      привело бы к расширению блока общей памяти в область, пред-
 
      шествующую существующему начальному  адресу  общей  области
 
      памяти.
 
 
           Примеры:
 
           1. Корректная   установка   эквивалентности   массивов
 
      NAME(6) и MESSAGE(10):
 
       BYTE MESSAGE (10)
 
       CHARACTER NAME (6)
 
       COMMON /MAIL_BOX/MESSAGE
 
       EQUIVALENCE (NAME, MESSAGE (8))
 
           Последние три элемента массива MESSAGE  и  первые  три
 
      элемента массива NAME совпадают, общая область  расширяется
 
      на три элемента массива NAME в сторону увеличения адресов.
 
           2. Некорректная  установка  эквивалентности   массивов
 
      NAME(6) и MESSAGE(10):
 
           BYTE MESSAGE (10)
 
           CHARACTER NAME (6)
 
           COMMON /MAIL_BOX/MESSAGE
 
           EQUIVALENCE (NAME (4), MESSAGE)
 
           Требуется совпадение последних трех элементов  массива
 
      NAME с первыми тремя элементами из общей области, что  при-
 
      водит к недопустимому расширению общей  области  на  первые
 
      три элемента массива NAME в сторону уменьшения адресов.
 


                                - 81 -
 
                          00152-01 97 09-1
 
 
           2.1.5. Непрерывное расположение
 
 
           Переменные не обязательно хранятся в виртуальной памя-
 
      ти в том порядке, в котором они описаны. Если порядок  этот
 
      является существенным  (например,  при  создании  структуры
 
      данных), то нужно задавать переменную в блоке общей  памяти
 
      или в массиве в порядке желательного расположения.
 
 
 
           2.1.6. Большие структуры данных и динамическая память
 
 
           Память для всех данных  в  СП  ФОРТРАН  распределяется
 
      статистически, то есть память отводится в момент  получения
 
      значения этой переменной и сохранияется за ней во все время
 
      выполнения программы. Статистическая память не требует мес-
 
      та в файле образа (файл, содержащий выполняемую  прграмму),
 
      если элемент данных не был инициализирован (например,  опе-
 
      ратором DATA). Однако хранение  всех  данных  требует  вир-
 
      туальной памяти.
 
           Примечание.  Размер  виртуальной  памяти  определяется
 
      системным параметром VIRTUALPAGECNT.  Половина  виртуальной
 
      памяти резервируется для системных применений и обычно  для
 
      пользователя недоступна. От оставшейся  половины  автомати-
 
      чески отводится половина пользователю, а вторую половину он
 
      может динамически распределять.
 
           Обычно виртуальной памяти хватает, чтобы гарантировать
 
      достаточно места для всех переменных. Например,  если  ожи-
 
      дается, что  понадобится  хранить  до  2048  действительных
 
      чисел   в   переменных   PERSON_HOUSE,    ADULTS_HOUSE    и
 
      INCOME_HOUSE, нужно отвести  под  них  массивы  размерности
 
      2048:
 


                                - 82 -
 
                          00152-01 97 09-1
 
 
       REAL PERSONS_HOUSE(2O48),
 
       2    ADULTS_HOUSE(2O48),
 
       2    INCOME_HOUSE(2O48)
 
           При недостатке виртуальной памяти необходимо  рассмот-
 
      реть возможность уменьшения структур данных и обработки  их
 
      не сразу, а по частям. Если  используются  большие  массивы
 
      или строки символов, которые не нужны  на  всем  протяжении
 
      выполнения программы,  можно  отвести  под  эти  переменные
 
      память  динамически,  используя  процедуры   LIB$GET_VM   и
 
      LIB$FREE_VM из библиотеки исполнительных процедур.
 
           Динамическое выделение и освобождение памяти  особенно
 
      важно для обслуживающих подпрограмм общего назначения,  так
 
      как эти подпрограммы не могут предвидеть потребности голов-
 
      ной программы в памяти и не должны перегружать образ ненуж-
 
      ной статической памятью. Процедура LIB$GET_VM выделяет  или
 
      закрепляет участок памяти требуемого размера (в  байтах)  и
 
      возвращает адрес  первого  закрепленного  байта.  Процедура
 
      LIB$FREE_VM  освобождает  память,  закрепленную  процедурой
 
      LIB$GET_VM. Адрес, возвращенный процедурой  LIB$GET_VM,  не
 
      может быть непосредственно использован в программе на языке
 
      ФОРТРАН, но его  можно  передать  в  подпрограмму,  которая
 
      получит доступ к закрепленной памяти.
 
 
 
           2.1.6.1. Динамическое хранение числовых данных
 
 
           Для захвата и  освобождения  динамической  памяти  под
 
      числовые данные необходимо предпринять следующие действия:
 
           1) часть программы, обращающуюся к динамической  памя-
 
      ти, нужно вынести в подпрограмму;
 
           2) вызвать LIB$GET_VM для получения начального  адреса
 


                                - 83 -
 
                          00152-01 97 09-1
 
 
      указанного количества байтов динамической памяти;
 
           3)  вызвать  подпрограмму,  использующую  динамическую
 
      память  и  передать  ей  адрес,   возвращенный   процедурой
 
      LIB$GET_VM, и количество отведенных байтов.  Адрес  следует
 
      передавать с использованием встроенной функции языка  ФОРТ-
 
      РАН %VAL;
 
           4) в подпрограмме определить формальный параметр, свя-
 
      занный с адресом отведенной области как числовое  значение,
 
      имеющее тот же размер, что и число отведенных байтов. Обыч-
 
      но этот формальный параметр описывается как массив перемен-
 
      ной или воспринимаемой длины требуемого типа;
 
           5) в вызывающей программе освободить память  после  ее
 
      использования вызовом процедуры LIB$FREE_VM.
 
 
           Пример.
 
           Головная программная единица вызывает  LIB$GET_VM  для
 
      отведения 512 байтов памяти, а затем передает адрес и вели-
 
      чину отведенной области подпрограмме,  которая  эту  память
 
      использует.
 
      ! Программа получения динамической памяти
 
      PROGRAM GETMEM
 
      ! Описание переменных
 
      INTEGER STATUS,
 
      2       VM_SIZE, !Размер памяти в байтах
 
      2       VM_ADDR  !Адрес в памяти
 
      ! Описать процедуру пользователя
 
      INTEGER USEMEM
 
      ! Описать библиотечную процедуру
 
      INTEGER LIB$GET_VM,
 
      2       LIB$FREE_VM
 


                                - 84 -
 
                          00152-01 97 09-1
 
 
      ! Занять память
 
      VM_SIZE = 512
 
      STATUS=LIB$GET_VM (VM_SIZE,
 
      2                  VM_ADDR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Использовать память
 
      STATUS=USEMEM (%VAL(VM_ADDR,
 
      2               VM_SIZE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Освободить память
 
      STATUS=LIB$FREE_VM (VM_SIZE,
 
      2               VM_ADDR
 
      IF (NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !
 
      ! Продолжение программы
 
      !
 
      ! Подпрограмма использования динамической памяти:
 
      INTEGER FUNCTION USEMEM (ARRAY,
 
      2                        ARRAY_SIZE)
 
      ! Описание формальных параметров
 
      INTEGER*4 ARRAY_SIZE,
 
      2         ARRAY(ARRAY_SIZE/4)
 
      !Установить код возврата нормального состояния
 
      USEMEM = 1
 
      !
 
      ! Продолжение подпрограммы  .
 


                                - 85 -
 
                          00152-01 97 09-1
 
 
           2.1.6.2. Динамическое хранение символьных данных
 
 
           Для динамического закрепления  и  освобождения  памяти
 
      под символьные данные необходимо:
 
           1) часть программы, использующую эту память, вынести в
 
      подпрограмму;
 
           2) вызвать LIB$GET_VM  для  получения  адреса  области
 
      памяти заданного размера (в байтах);
 
           3) вызвать подпрограмму,  использующую  эти  данные  и
 
      передать ей дескриптор символьных данных.  Дескриптор  есть
 
      квадрослово, первое двойное слово которого  содержит  коли-
 
      чество байтов  для  передачи,  а  второе  двойное  слово  -
 
      начальный   адрес   динамической    памяти,    возвращенной
 
      LIB$GET_VM;
 
           4) в подпрограмме описать соответствующий  дескриптору
 
      формальный параметр как строку символов передаваемой  длины
 
      или как символьную переменную соответствующей  длины.  Если
 
      формальный параметр,  отвечающий  дескриптору,  описан  как
 
      массив типа CHARACTER, то он должен быть массивом восприни-
 
      маемой длины или массивом переменной длины;
 
           5) в вызывающей программе  освободить  память  вызовом
 
      LIB$FREE_VM.
 
 
           Пример.
 
           Вызывающая программная единица GETMEM передает функции
 
      USEMEM дескриптор как массив VM_DESC; первый элемент масси-
 
      ва содержит размер (VM_SIZE), а второй - адрес (VM_ADDR):
 
      ! Программа получения динамической памяти
 
      PROGRAM GETMEM
 
      ! Описание переменных
 
      INTEGER STATUS,


                                - 86 -
 
                          00152-01 97 09-1
 
 
      2       VM_SIZE,  ! Размер области памяти в байтах
 
      2       VM_ADDR   ! Адрес области памяти
 
      ! Описание процедуры пользователя
 
      INTEGER USEMEM
 
      ! Описание библиотечных процедур
 
      INTEGER LIB$GET_VM,
 
      2       LIB$GET_VM
 
      ! Описание дескриптора
 
      INTEGER VM_DESC (2)
 
      ! Закрепить память
 
      VM_SIZE = 512
 
      STATUS = LIB$GET_VM (VM_SIZE,
 
      2                  VM_ADDR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Использовать память
 
      VM_DESC(1) = VM_SIZE  ! Размер
 
      VM_DESC(2) = VM_ADDR  ! Начало
 
      STATUS = USEMEM (VM_DESC)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Освободить память
 
      STATUS = LIB$FREE_VM (VM_SIZE,
 
                            VM_ADDR)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !
 
      ! Продолжение программы
 
      !
 
      INTEGER FUNCTION USEMEM (ARRAY)
 
      ! Подпрограмма для использования динамической памяти
 
      ! Описание формальных параметров
 
      CHARACTER*8 ARRAY(*)


                                - 87 -
 
                          00152-01 97 09-1
 
 
      ! Установить код возврата нормального состояния
 
      USEMEM = 1
 
      !
 
      ! Продолжение подпрограммы   .
 
 
 
           2.1.6.3. Статистика динамической памяти
 
 
           У пользователя есть возможность следить за количеством
 
      вызовов как LIB$GET_VM, Tак и LIB$FREE_VM, а также за коли-
 
      чеством занятых и освобожденных байтов с помощью  процедуры
 
      LIB$STAT_VM или LIB$SHOW_VM. Для получения одной из статис-
 
      тик в двоичном виде используется LIB$STAT_VM. Для получения
 
      всех или одной статистики, оформленных в виде строки симво-
 
      лов, используется LIB$SHOW_VM.
 
 
           Пример.
 
      ! Показать используемую память
 
      STATUS = LIB$SHOW_VM()
 
           LIB$SHOW_VM  возвращает  сообщение,  передаваемое   по
 
      умолчанию в SYS$OUTPUT,  но  можно  написать  подпрограмму,
 
      которая это умолчание изменит [2]:
 
      3 LIB$GET_VM CALLS, 0 LIB$FREE_VM CALLS, 952 BYTES STILL
 
      ALLOCATED
 
      (3 вызова LIB$GET_VM, 0 вызовов LIB%FREE_VM, 952 занятых
 
      байта).
 


                                - 88 -
 
                          00152-01 97 09-1
 
 
           2.2. Последовательное выполнение
 
 
           Обычно операторы выполняются в том порядке, в  котором
 
      они написаны в программной единице  -  последовательно,  от
 
      входной точки программной единицы и до  операторов  END.  С
 
      целью улучшения структуры программной единицы необходимо:
 
           1) выделять каждое  действие  в  визуальный  отдельный
 
      блок операторов (блоки должны разделяться комментарием  или
 
      пустыми строками) и предварять каждый блок комментарием;
 
           2) выделять сложные куски  программы,  оформляя  их  в
 
      отдельные подпрограммы.
 
 
           Пример.
 
           Выполняемые операторы функции CONVERT.FOR сгруппирова-
 
      ны по действиям, поясненных коментариями:
 
 
      ! Инициализация состояния
 
      STATUS = STATUS_OK
 
 
 
      ! Преобразование количества человек на дом
 
      READ (UNIT = FIX_PERSONS_STRING,
 
      2     FMT = INTEGER_FMT) FIX_PERSONS_HOUSE
 
 
 
      ! Преобразование количества взрослых на дом
 
      READ (UNIT = FIX_ADULTS_STRING,
 
      2     FMT = INTEGER_FMT) FIX_ADULTS_HOUSE
 
 
 
      ! Преобразование дохода на дом
 
      READ (UNIT = FIX_INCOME_STRING,
 
      2     FMT = INTEGER_FMT) FIX_INCOME_HOUSE
 
 
 


                                - 89 -
 
                          00152-01 97 09-1
 
 
      CONVERT_FIXES = STATUS
 
      END ! Конец функции .
 
           Хотя  последовательное  выполнение  можно   обойти   с
 
      помощью операторов ENTRY, RETURN  и  GOTO,  такие  передачи
 
      управления затрудняют слежение за программой и не  рекомен-
 
      дуются.
 
 
 
           2.3. Условное выполнение
 
 
           Условное выполнение дает  возможность  выполнить  один
 
      или более операторов (блок  операторов)  в  зависимости  от
 
      заданного условия. Если условие справедливо,  то  следующий
 
      за ним блок  операторов  выполняется,  в  противном  случае
 
      выполнение передается на выполняемый оператор, следующий за
 
      блоком.
 
           Логика условного выполнения осуществляется в  основном
 
      за счет операторов IF, ELSE, ELSE IF.
 
 
 
           2.3.1. Задание условия
 
 
           Выполнение блока операторов может быть связано с одним
 
      условием или множеством условий. Каждое условие есть  выра-
 
      жение, вычисляемое в виде логического  значения.  Например,
 
      оператор READ выполняется, если логическое значение  STATUS
 
      истинно:
 
      IF (STATUS) THEN
 
      READ (UNIT = FIX_PERSONS_STRING,
 
      2     FMT = INTEGER_FMT) FIX_PERSONS_HOUSE
 
      END IF
 
      или оператор присваивает значение переменной  STATUS,  если
 
      истинно логическое выражение:


                                - 90 -
 
                          00152-01 97 09-1
 
 
      IF (HOUSE_NO .GT. MAXSTAT) THEN
 
      STATUS = %LOC (INCOME_MAXSTATS)
 
      END IF
 
           Чтобы связать выполнение оператора с группой  условий,
 
      используются логические операции .AND., .OR., .XOR., .NEQV.
 
      И .EQV., с помощью которых условия комбинируются. Например,
 
      для выполнения блока операторов требуется,  чтобы  значение
 
      STATUS было истинным, а значение LINE_COUNT было больше 58:
 
      IF ((STATUS) .AND. (LINE_COUNT .GT. 58)) THEN
 
      ! Выполняемый
 
      ! Блок операторов
 
      !
 
      END IF !  .
 
           В операторе IF можно использовать  произвольное  число
 
      логических операций. Например, для выполнения блока  опера-
 
      торов должны быть выполнены два условия -  значение  STATUS
 
      должно быть6+стинным и либо значение NEW_PAGE  должно  быть
 
      истинным, либо LINE_COUNT должно быть больше 58:
 
      ! Записать новое STATS.SAV
 
      IF ( (STATUS) .AND.
 
      2    ((NEW_PAGE) .OR. (LINE_COUNT .GT. 58)) ) THEN
 
      ! Выполняемый
          
      ! Блок операторов
 
      !
 
      END IF
 
           Скобки меняют порядок интерпретации условий; даже если
 
      этот порядок  менять  не  нужно,  скобки  поясняют  порядок
 
      интерпретации, определяя порядок выполнения  от  внутренних
 
      скобок к внешним.
 


                                - 91 -
 
                          00152-01 97 09-1
 
 
           2.3.2. Условный логический оператор
 
 
           Простейшая форма оператора IF (логический  IF)  прове-
 
      ряет условие и, если условие истинно, выполняет один опера-
 
      тор.
 
           Формат:
 
      IF (условие) оператор
 
           Оператор, следующий за  условием,  может  быть  любым,
 
      кроме  DO,  END  DO,  END   или   IF.   Например,   функция
 
      LIB$ERASE_LINE выполняется только в том случае, когда логи-
 
      ческое значение STATUS есть истина:
 
      IF (STATUS) STATUS = LIB$ERASE_LINE (LINE_NO,
 
      2                                    END_COLUMN)
 
 
 
           2.3.3. Условные блочные операторы
 
 
           ФОРТРАН МОС ВП располагает включающими и  исключающими
 
      условными блочными операторами.
 
           Условные блочные операторы (условные блоки) предпочти-
 
      тельнее условного логического оператора,  поскольку  позво-
 
      ляют конструировать наглядные, легко читаемые и  достаточно
 
      сложные (вложенные) условные структуры.
 
 
 
           2.3.3.1. Исключающие условные блоки
 
 
           Исключающие  условные  структуры  позволяют  выполнять
 
      один блок операторов из нескольких. В простейшей форме иск-
 
      лючающая условная структура позволяет выбрать для  выполне-
 
      ния один условный блок из двух.
 
           Формат:
 
      IF (условие) THEN
 


                                - 92 -
 
                          00152-01 97 09-1
 
 
      !
 
      ! Блок операторов 1
 
      !
 
      ELSE
 
      !
 
      ! Блок операторов 2
 
      !
 
      END IF
 
           Если условие верно, то выполняется первый блок  опера-
 
      торов, в противном случае выполняется второй блок.
 
           Другая форма исключающей условной  конструкции  позво-
 
      ляет выполнить блок операторов, следующий за первым  истин-
 
      ным условием. Если не верно ни одно  условие,  то  ни  один
 
      блок операторов не будет выполняться. Если же  верно  более
 
      чем одно условие, то будет выполняться только блок, стоящий
 
      за первым верным условием.
 
           Формат:
 
      IF (условие)
 
      !
 
      ! Блок операторов  1
 
      !
 
      ELSE IF (условие)THEN
 
      !
 
      ! Блок операторов  2
 
      ! Необязательные операторы ELSE IF
 
      !
 
      END IF
 


                                - 93 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Проверка кода завершения IOSTAT операции ввода-вывода:
 
      если первое условие верно, то  переменной  STATUS  присваи-
 
      вается значение INCOME_CTRLZ независимо от второго условия;
 
      если же первое  условие  ложно,  то,  если  второе  условие
 
      истинно,  STATUS  присваивается  значение  INCOME_FORIOERR;
 
      если же и второе условие ложно, то STATUS остается неизмен-
 
      ным:
 
      INTEGER*4 IOSTAT,
 
      2       IO_OK,
 
      2       EOF,
 
      2       STATUS
 
      EXTERNAL INCOME FORIOERR,
 
      2       INCOME_CTRLZ
 
      !
 
      ! Возможные описания
 
      !
 
      IF (IOSTAT .EQ. EOF) THEN
 
      STATUS = %LOC (INCOME_CTRLZ)
 
      ELSE IF (IOSTAT .NE. IO_OK) THEN
 
      STATUS = %LOC (INCOME_FORIOERR)
 
      END IF
 
           В  третьей  форме  исключающей  условной   конструкции
 
      выполняется первый условный  блок,  следующий  за  истинным
 
      условием. Если же  истинного  условия  не  оказывается,  то
 
      выполняется блок операторов, следующий за оператором ELSE.
 
           Формат:
 
      IF (условие) THEN
 
      !
 
      ! Блок операторов 1


                                - 94 -
 
                          00152-01 97 09-1
 
 
      !
 
      ELSE IF (условие)
 
      !
 
      !
 
      ! Блок операторов 2
 
      ! Возможный оператор ELSE IF
 
      !
 
           ЕLSE
 
      !
 
      ! Блок операторов 3
 
      !
 
      END IF
 
 
           Пример.
 
           Исключающая  исходная  конструкция  используется   для
 
      вызова соответствующей подпрограммы в зависимости от  того,
 
      какой квалификатор введен в командной строке:
 
      ! Ввод квалификатора и передача управления
 
      ! Соответствующей подпрограмме:
 
      ! Если пользователь ввел /ENTER
 
      IF (CLI$PRESENT ('ENTER')) THEN
 
        STATUS=GET_STATS (TOTAL_HOUSES,
 
      !
 
      ! Блок операторов
 
      !
 
      ! Если пользователь ввел /FIX
 
      ELSE IF (CLI$PRESENT ('FIX')) THEN
 
        STATUS=CLI$GET_VALUE ('FIX',
 
      !
 
      ! Блок операторов
 


                                - 95 -
 
                          00152-01 97 09-1
 
 
      !
 
      ! Если пользователь ввел /REPORT
 
      ELSE IF (CLI$PRESENT ('REPORT')) THEN
 
        STATUS=REPORT (TOTAL_HOUSE,
 
      !
 
      ! Блок операторов
 
      !
 
      ELSE
 
        STATUS = %LOC (INCOME_NOACTION)
 
      END IF
 
           Оператор, следующий за  оператором  ELSE,  выполняется
 
      лишь тогда, когда  не  введен  ни  один  из  квалификаторов
 
      /INTER, /FIX и /REPORT. Если введены одновременно  квалифи-
 
      каторы /ENTER, /REPORT, то будет выполняться только  первый
 
      блок (ENTER);  для  выполнения  обоих  блоков  используется
 
      включающая условная конструкция.
 
 
 
           2.3.3.2. Включающие условные блоки
 
 
           Включающая  условная  конструкция   дает   возможность
 
      выполнять каждый блок, следующий за истинным условием.
 
           Формат:
 
      IF (условие) THEN
 
      !
 
      ! Блок операторов
 
      !
 
      END IF
 
           В этой условной конструкции за условием должно  следо-
 
      вать ключевое слово THEN, затем блок  операторов,  заканчи-
 
      вающийся оператором END IF.
 


                                - 96 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Вызов соответствующей подпрограммы для каждого  квали-
 
      фикатора, встреченного в командной строке:
 
      ! Анализ квалификатора
 
      IF (CLI$PRESENT ('ENTER')) THEN
 
        STATUS = GET_STATUS (TOTAL_HOUSES.
 
      !
 
      ! Блок операторов
 
      !
 
      END IF
 
      IF (CLI$PRESENT ('FIX')) THEN
 
        STATUS = CLI$GET_VALUE ('FIX',
 
      !
 
      ! Блок операторов
 
      !
 
      END IF
 
      IF (CLI$PRESENT ('REPORT')) THEN
 
        STATUS = REPORT (TOTAL_HOUSE,
 
      !
 
      ! Блок операторов
 
      !
 
      END IF
 
      ! Проверка на ошибку
 
      IF ((.NOT. CLI$PRESENT ('ENTER')) .AND.
 
      2   (.NOT. CLI$PRESENT ('FIX')) .AND.
 
      2   (.NOT. CLI$PRESENT ('REPORT'))) THEN
 
        STATUS = %LOC (INCOME_NOACTION)
 
      END IF
 
           Поскольку включающая условная конструкция не  содержит
 
      оператора ELSE, стоящий за которым блок  операторов  выпол-


                                - 97 -
 
                          00152-01 97 09-1
 
 
      нялся бы при отрицании каждого условия, заданного во  вклю-
 
      чающей условной конструкции, необходимо проверять  одновре-
 
      менное отрицание каждого из этих  условий.  Другой  способ:
 
      установить STATUS равным INCOME_NOACTION в начале программ-
 
      ного сегмента; если не задан ни один квалификатор, значение
 
      STATUS не изменится.
 
 
 
           2.4. Циклические операторы
 
 
           Циклические операторы обеспечивают повторное  выполне-
 
      ние блоков оператора до тех  пор,  пока  остается  истинным
 
      заданное логическое условие.
 
           В  языке  ФОРТРАН  есть  несколько  форм   циклических
 
      DO-операторов, структура  которых  подобна  приведенной  на
 
      рис. 3.
 
           Проверка в начале блока операторов или  цикла  опреде-
 
      ляет, будут ли выполняться операторы внутри цикла. Перемен-
 
      ная, управляющая результатом этой  проверки  -  управляющая
 
      переменная - инициализируется перед входом в цикл; в  цикле
 
      изменяется с целью возможного прекращения выполнения цикла.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 98 -
 
                          00152-01 97 09-1
 
 
 
 
 
 
             Логическая структура циклических операторов
 
                      ---------------------------
                      !     Инициализация       !
                      !                         !
                      ! управляющей переменной  !
                      ---------------------------
                                  !
             ^--------------------!
             !                    !
             !                    !
             !                   / \
             !                  /   \
             !                /       \
             !              /  Проверка \   ложь
             !             <   условия   >-------------
             !              \    DO     /             !
             !                \       /               !
             !                  \   /                 !
             !                    V                   !
             !             истина !                   !
             !                    !                   !
             !       ----------------------------     !
             !       !                          !     !
             !       !   Блоки операторов       !     !
             !       !                          !     !
             !       !                          !     !
             !       !   Изменение управляющей  !     !
             !       !                          !     !
             !       !   переменной             !     !
             !       !                          !     !
             !       !   END DO                 !     !
             !       ----------------------------     !
             !                    !                   !
             <--------------------!                   !
                                                      !
                                  <--------------------
                                  !
                                  !
                                  !
 
                                Рис. 3
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 99 -
 
                          00152-01 97 09-1
 
 
           2.4.1. Оператор DO WHILE
 
 
           Циклический оператор DO WHILE повторно выполняет  опе-
 
      раторы, составляющие тело цикла, до тех пор, пока  справед-
 
      ливо заданное условие; когда это условие становится ложным,
 
      управление передается следующему за телом цикла выполняемо-
 
      му оператору.
 
           Формат:
 
      (инициализация управляющей переменной)
 
      DO WHILE (условие)
 
      !
 
      ! Блок операторов
 
      ! Изменение управляющей переменной
 
      END DO
 
           Оператором, ограничивающим цикл DO, должен быть опера-
 
      тор END DO.
 
           Пример.
 
           Символы из строки STRING рассматриваются до  тех  пор,
 
      пока не будет обнаружен символ, отличный от пробела, а зна-
 
      чение FIRST_DIGIT не превосходит SIZE:
 
      ! Нахождение первой цифры числа
 
      FIRST_DIGIT = 1
 
      DO WHILE ((STRING (FIRST_DIGIT:FIRST_DIGIT) .EQ. ' ') .AND.
 
      2         (FIRST_DIGIT .LE. SIZE))
 
        FIRST_DIGIT = FIRST_DIGIT + 1
 
      END DO   .
 
           Заметим, что FIRST_DIGIT получает начальное значение 1
 
      перед циклом, а при каждом выполнении цикла на единицу уве-
 
      личивается. Если символ не найден, FIRST_DIGIT  равна  SIZE
 
      плюс 1.
 


                                - 100 -
 
                          00152-01 97 09-1
 
 
           Сдвиг вправо операторов тела цикла DO делает организа-
 
      цию этого блока более наглядной. Это особенно  полезно  при
 
      работе с вложенными циклами DO.
 
 
 
           2.4.2. Оператор DO
 
 
           Шаговый оператор цикла DO выполняет оператор или  блок
 
      операторов заданное число раз.
 
           Формат
 
      DO упр=начало, конец [,шаг]
 
      !
 
      ! Блок операторов
 
      !
 
      END DO
 
           В таком операторе DO управляющая переменная  инициали-
 
      зируется  и  изменяется  автоматически.  Переменная  "упр",
 
      которая должна быть целой или вещественной, является управ-
 
      ляющей. Значение "начало" присваивается  управляющей  пере-
 
      менной перед выполнением цикла. Значение "шаг", положитель-
 
      ное или отрицательное, прибавляется к переменной "упр" пос-
 
      ле каждого выполнения  цикла;  по  умолчанию  это  значение
 
      полагается равным +1. Значение "конец"  представляет  собой
 
      значение управляющей  переменной  после  выхода  из  цикла.
 
      "начало", "конец" и "шаг" являются числовыми значениями.
 
           Примеры:
 
           1. В шаговом операторе DO блок операторов  выполняется
 
      до  тех  пор,  пока   I   меньше   или   равно   переменной
 
      TOTAL_HOUSES.  Управляющей   переменной   I   присваивается
 
      начальное значение 1, которое увеличивается на 1 при каждом
 
      проходе цикла. Отметим, что I используется как индекс  мас-
 


                                - 101 -
 
                          00152-01 97 09-1
 
 
      сивов PERSONS_HOUSE и ADULTS_HOUSE:
 
 
      DO I = 1, TOTAL_HOUSE
 
      PERSONS = PERSONS + PERSONS_HOUSE (I)
 
      ADULTS = ADULTS + ADULTS_HOUSE(I)
 
      END DO
 
 
           2. Оператор DO WHILE эквивалентен предыдущему шаговому
 
      оператору DO:
 
 
      I = 1
 
      DO WHILE (I .LE. TOTAL_HOUSES)
 
      PERSONS =  PERSONS + PERSONS_HOUSE (I)
 
      ADULTS = ADULTS + ADULTS_HOUSE (I)
 
      I = I + 1
 
      END DO
 
 
 
           2.5. Вложенные конструкции
 
 
           Условные конструкции IF и  операторы  цикла  DO  могут
 
      быть вложены в аналогичные структуры, а также друг в друга.
 
      В любом случае всякая вложенная структура должна  полностью
 
      содержаться во внешней конструкции. Для наглядности  чтения
 
      программы  для  каждой  вложенной  конструкции   необходимо
 
      писать операторы с отступлением от левого края поля и  ком-
 
      ментировать каждый оператор END.
 
 
           Примеры:
 
           1. Правильное вложение циклического оператора DO WHILE
 
      в условный блок:
 
      IF (условие) THEN
 
      !
 


                                - 102 -
 
                          00152-01 97 09-1
 
 
      ! Выполняемые операторы
 
      !
 
           DO WHILE (условие)
 
      !
 
      ! Выполняемые операторы
 
      !
 
           END DO
 
      END IF
 
           2. Неправильное перекрытие циклического  оператора  DO
 
      WHILE и условного включающего блока:
 
      IF (условие) THEN
 
      !
 
      ! Выполняемые операторы
 
      !
 
           DO WHILE (условие)
 
      END IF
 
           END DO
 
           3. Исключающая условная конструкция вложена в  одиноч-
 
      ный условный блок, который вложен в цикл DO WHILE:
 
      DO WHILE (условие)
 
        READ (UNIT=STRING (1:STRING_SIZE),
 
      2       FMT=INTEGER_FMT,
 
      2       IOSTAT=IOSTAT) STAT
 
 
 
        ! Стирание бессмысленных символов
 
        STATUS = LIB$ERASE_LINE (LINE_NO,
 
      2                          END_COLUMN)
 
        IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
        ! При неудаче ввода/вывода
 
        IF (IOSTAT .NE. IO_OK) THEN


                                - 103 -
 
                          00152-01 97 09-1
 
 
          CALL ERRSNS (,,,,STATUS)
 
        IF(STATUS .EQ. FOR$_INPCONERR) THEN
 
          ! Повторять пока пользователь не ответит
 
          ! Приемлемым вводом,
 
          ! Или не нажмет CTRL/Z
 
          STATUS = LIB$ERASE_LINE(LINE_NO,
 
      2                           COLUMN_NO)
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          STATUS = LIB$PUT_SCREEN ('условие!',
 
      2                            LINE_NO,
 
      2                            END_COLUMN)
 
          IF (.NOT.STATUS)CALL LIB$SIGNAL (%VAL (STATUS))
 
          условие = .TRUE.
 
        ELSE
 
          ! При неожиданной ошибке чтения
 
          IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
          END IF ! STATUS .EQ. FOR$_INPCONERR
 
        END IF ! IOSTAT .NE. IO_OK
 
      END DO ! Конец цикла (условие)  .
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 104 -
 
                          00152-01 97 09-1
 
 
         3. Синхронизация работы программ и связи между ними
 
 
 
           Способы синхронизации программ и сообщения между  ними
 
      меняются в зависимости от того,  находятся  ли  программные
 
      единицы, о которых идет речь, в одной программе, в  различ-
 
      ных программах, выполняемых в одном и том же процессе,  или
 
      в различных программах из различных процессов.  Если  нужно
 
      выполнить две или больше программ,  то  это  можно  сделать
 
      последовательно с использованием одного процесса,  последо-
 
      вательно с использованием нескольких процессов и параллель-
 
      но с использованием нескольких процессов. Так как процесс в
 
      каждый момент времени исполняет в точности одну  программу,
 
      то две или больше программы в одном  процессе  одновременно
 
      выполнять нельзя.
 
 
 
           3.1. Создание процесса
 
 
           Создание процесса изнутри программы дает возможность:
 
           1) имитировать функции командного уровня (создать про-
 
      цесс, выполняющий команды DCL);
 
           2) проводить параллельную обработку (создать  процесс,
 
      выполняющий часть задания, в то время как процесс, его соз-
 
      давший, продолжает выполнять другую часть);
 
           3) осуществить многопользовательскую  работу  (создать
 
      свой процесс для каждого прикладного пользователя.  Создаю-
 
      щий процесс может  координировать  данные,  поступающие  от
 
      созданных процессов);
 
           4) задать время выполнения программы (создать процесс,
 
      выполняющий программу, а затем перевести его в спячку. Про-
 
      буждая этот процесс в заданное  время  или  через  заданные
 


                                - 105 -
 
                          00152-01 97 09-1
 
 
      временные интервалы, можно контролировать время  выполнения
 
      программы);
 
           5) выделить часть программы (создать  процесс,  выпол-
 
      няющий привиллигированные или чувствительные команды).
 
           Созданный процесс может быть подпроцессом или отсоеди-
 
      ненным процессом.  Для  создания  подпроцесса  используется
 
      процедура из библиотеки исполнительных  процедур  LIB$SPAWN
 
      (дублирует  команду  DCL  SPAWN)  или  системную  процедуру
 
      SYS$CREPRC.  Для  создания  отсоединенного  процесса  нужно
 
      использовать SYS$CREPRC.
 
           Подпроцесс зависит от породившего его процесса и  уда-
 
      ляется, когда заканчивается работа родительского  процесса.
 
      Поэтому, если необходимо, чтобы созданный процесс продолжал
 
      выполнение программы после окончания работы породившего его
 
      процесса, нужно создать отсоединенный процесс.
 
 
 
           3.1.1. Создание подпроцесса
 
 
           Подпроцесс можно создать с помощью процедур  LIB$SPAWN
 
      или SYS$CREPRC. Как показано в  табл.4,  LIB$SPAWN  создает
 
      более полный контекст для подпроцесса (предполагается,  что
 
      при вызове LIB$SPAWN и  SYS$CREPRC  действуют  принятые  по
 
      умолчанию соглашения).
 
                                                        Таблица 4
 
                  Контекст создаваемого подпроцесса
 
      -----------------------------------------------------------
                          !                 !
         Контекст         !  LIB$SPAWN      !     SYS$CREPRC
                          !                 !
      -----------------------------------------------------------
                          !                 !
         DCL              !   Да            !   Нет
                          !                 !
         Устройство и     !   Породившего   !   Породившего про-
                          !                 !


                                - 106 -
 
                          00152-01 97 09-1
 
 
                                              Продолжение табл. 4
      -----------------------------------------------------------
                          !                 !
         Контекст         !  LIB$SPAWN      !     SYS$CREPRC
                          !                 !
      -----------------------------------------------------------
       каталог по умол-   ! процесса        ! цесса
                          !                 !
       чанию              !                 !
                          !                 !
         Идентификаторы   !   Породившего   !   Нет
                          !                 !
                          ! процесса        !
                          !                 !
         Логические имена !   Породившего   !   Нет
                          !                 !
                          ! процесса        !
                          !                 !
         Привилегии       !   Породившего   !   Породившего про-
                          !                 !
                          ! процесса        ! цесса
                          !                 !
         Приоритет        !   Породившего   !   0
                          !                 !
                          ! процесса        !
                          !                 !
 
           Примечания:
 
           1. Контекст создаваемого  подпроцесса  может  включать
 
      DCL      путем      выполнения      системного       образа
 
      SYS$SYSTEM:LOGINOUT.EXE.
 
           2. При наследовании логических имен наследуются  также
 
      таблицы групповых имен и логических имен заданий.
 
           Обычно при вызове LIB$SPAWN употребляются только  пер-
 
      вые четыре аргумента:
 
           - командная строка (первый аргумент). Задается  коман-
 
      да, которая должна будет выполняться в созданном подпроцес-
 
      се. Для выполнения группы команд вызывается командная  про-
 
      цедура (@COMMAND_PROCEDURE);
 
           - SYS$INPUT и SYS$OUTPUT (второй и третий  аргументы).
 
      Задаются эквивалентные имена подпроцесса  для  SYS$INPUT  и
 
      SYS$OUTPUT. Если  эти  аргументы  опустить,  то  подпроцесс
 
      унаследует эквивалентные имена от  родительского  процесса.
 


                                - 107 -
 
                          00152-01 97 09-1
 
 
      если задать SYS$INPUT как  файл  (или  нетерминальное  уст-
 
      ройство), подпроцесс будет создан как неинтерактивный  про-
 
      цесс. Эти аргументы соответствуют квалификаторам  /INPUT  и
 
      /OUTPUT командной строки SPAWN DCL.
 
           Логические  имена  SYS$INPUT  и  SYS$OUTPUT  полностью
 
      определяются этими двумя аргументами. На них не влияет зна-
 
      чение бита 2  четвертого  аргумента,  определяющего  другие
 
      логические имена для подпроцесса;
 
           - контекст и выполнение (четвертый аргумент). Задается
 
      маска, указывающая, наследует ли подпроцесс идентификаторы,
 
      логические имена и определения малой клавиатуры от породив-
 
      шего процесса, а также - выполняется ли подпроцесс одновре-
 
      менно с породившим процессом или во время спячки родительс-
 
      кого процесса.
 
 
           Пример.
 
           Создание   подпроцесса,   выполняющего   команды    из
 
      COMMANDS.COM, командной процедуры на принятом по  умолчанию
 
      в данный момент устройстве в принятом по умолчанию  катало-
 
      ге. Созданный процесс наследует идентификаторы,  логические
 
      имена (включая SYS$INPUT и SYS$OUTPUT),  определения  малой
 
      клавиатуры и другую контекстную информацию от родительского
 
      процесса. Подпроцесс выполняется во время спячки родительс-
 
      кого процесса:
 
      !Определить состояние и библиотечную программу
 
      INTEGER STATUS, LIB$SPAWN
 
      STATUS = LIB$SPAWN ('@COMMANDS')
 


                                - 108 -
 
                          00152-01 97 09-1
 
 
           3.1.1.1. Контекст подпроцесса
 
 
           Создание подпроцесса  с  помощью  процедуры  LIB$SPAWN
 
      может занять несколько секунд. Это время  можно  уменьшить,
 
      если указать, что созданный подпроцесс должен  унаследовать
 
      только часть родительского  контекста.  Если  задать  флаги
 
      (четвертый аргумент) как CLI$M_NOLOGNAM, то  подпроцесс  не
 
      унаследует определения логических имен  родительского  про-
 
      цесса  (логические  имена,  требуемые  подпроцессом,  можно
 
      поместить в таблицу логических имен задания LNM$JOB).
 
 
           Пример.
 
           Сегмент программы создает подпроцесс,  не  наследующий
 
      идентификаторы, логические имена и определения  малой  кла-
 
      виатуры от  создавшего  процесса.  Этот  процесс  читает  и
 
      выполняет  команды  из  командной  процедуры   COMMANDS.COM
 
      (идентификаторы CLI$ определены в модуле $CLIDEF  системной
 
      библиотеки об'ектных или разделяемых образов):
 
      !Маска для LIB$SPAWN;
 
      INTEGER MASK
 
      EXTERNAL CLI$M_NOCLISYM,
 
      2        CLI$M_NOLOGNAM,
 
      2        CLI$M_NOKEYPAD
 
      !Описание состояния и библиотечной процедуры:
 
      MASK=%LOC(CLI$M_NOCLISYM).OR.
 
      2    %LOC(CLI$M_NOLOGNAM).OR.
 
      2    %LOC(CLI$M_NOKEYPAD)
 
      STATUS = LIB$SPAWN ('@COMMANDS.COM',
 
      2           ,,
 
      2            MASK)
 


                                - 109 -
 
                          00152-01 97 09-1
 
 
           3.1.1.2. Выполнение подпроцесса
 
 
           Подпроцесс может  выполняться  либо  во  время  спячки
 
      породившего процесса (последовательно), либо одновременно с
 
      продолжением выполнения родительского процесса  (параллель-
 
      но). Подпроцесс будет выполняться последовательно, если  не
 
      задано обратное. Для параллельного  выполнения  подпроцесса
 
      задается флаговый параметр (четвертый аргумент) при  вызове
 
      LIB$SPAWN как CLI$M_NOWAIT. Если родительский процесс  дол-
 
      жен дождаться  информации  от  подпроцесса,  то  необходимо
 
      выполнять подпроцесс последовательно.  В  противном  случае
 
      предпочтительнее параллельное выполнение подпроцесса.
 
           Из параллельно  выполняемого  подпроцесса  можно  осу-
 
      ществлять терминальный ввод-вывод, однако это требует вклю-
 
      чения программы, синхронизирующей доступ к терминалу. Чтобы
 
      при параллельном выполнении подпроцесса  избежать  проблем,
 
      связанных с синхронизацией,  нужно  установить  эквивалент-
 
      ность имен подпроцесса SYS$INPUT  и  SYS$OUTPUT  файлу  или
 
      устройству, отличному от терминала.  При  вызове  LIB$SPAWN
 
      задание параметров "входной файл" и "выходной файл" не поз-
 
      воляет им быть по умолчанию приравненными  к  эквивалентным
 
      именам этих файлов родительского процесса (по  умолчанию  -
 
      терминал).
 
 
           Пример.
 
           Сегмент программы создает  подпроцесс  для  выполнения
 
      образа $DISK1:[USER.MATH] CALC.EXE. Программа  CALC  читает
 
      данные из DATA84.IN и записывает результаты  в  DATA84.RPT.
 
      Подпроцесс выполняется паралельно (CLI$M_NOWAIT определен в
 
      модуле $CLIDEF из библиотеки системных об'ектных или разде-
 
      ляемых модулей):


                                - 110 -
 
                          00152-01 97 09-1
 
 
      !Маска для LIB$SPAWN;
 
      EXTERNAL CLI$_NOWAIT
       
      !Описать состояние и библиотечную процедуру
 
      INTEGER STATUS, LIB$SPAWN
       
      STATUS = LIB$SPAWN ('RUN $DISK1:[USER.MATH]CALC',!Образ
 
      2                   'DATA84.IN',                 !Ввод
 
      2                   'DATA84.RPR',                !Вывод
 
      2                   %LOC(CLI$M_NOWAIT))      !Параллельно
 
 
 
           3.1.1.3. Отладка программы в подпроцессе
 
 
           Перед вызовом программы в подпроцессе ее следует отла-
 
      дить. Если необходимо отладить программу внутри  подпроцес-
 
      са,  то  нужно  приравнять  логические  имена   подпроцесса
 
      DBG$INPUT и DBG$OUTPUT терминалу.
 
           Когда подпроцесс выполняет программу, скомпилированную
 
      с отладчиком, отладчик читает входные данные с DBG$INPUT  и
 
      выводит выходные данные на DBG$OUTPUT.
 
           При параллельном выполнении подпроцесса  отладку  сле-
 
      дует ограничить программой из подпроцесса. Подсказка отлад-
 
      чика DBG> дает возможность различать ввод, требуемый  роди-
 
      тельским процессом, и ввод, требуемый подпроцессом.  Однако
 
      каждый раз, когда отладчик  будет  выводить  информацию  на
 
      экран, требуется нажать <RETURN>  для  получения  подсказки
 
      DBG. В действительности  нажатие  <RETURN>  перехватывается
 
      родительским процессом,  который  захватит  терминал  сразу
 
      после вывода на него информации из  подпроцесса.  Обработка
 
      <RETURN> родительским процессом дает возможность подпроцес-
 
      су получить контроль над термналом.
 


                                - 111 -
 
                          00152-01 97 09-1
 
 
           3.1.2. Синхронизация терминального ввода-вывода
 
 
           Если  для  параллельно  выполняемого   подпроцесса   в
 
      качестве устройства  ввода-вывода  указан  пользовательский
 
      терминал, то для гарантии того, что в каждый момент времени
 
      доступ к терминалу имеет только один процесс,  используется
 
      диспетчер захвата. Так как родительский процесс и  сооздан-
 
      ный  подпроцесс  должны  использовать  системную  служебную
 
      программу диспетчера захвата для синхронизации  итерминаль-
 
      ного ввода-вывода, то процесс  должен  выполнять  пользова-
 
      тельскую программу, включающую синхронизацию вместо  списка
 
      команд DCL.
 
           Диспетчер  захвата  дает  возможность  взаимосвязанным
 
      процессам синхронизировать доступ к разделяемому ресурсу (к
 
      файлу, программе  или  устройству).  Когда  взаимосвязанные
 
      процессы ссылаются на ресурс, они это делают с  использова-
 
      нием согласованного имени. Диспетчер захвата работает толь-
 
      ко с этим именем, над самим ресурсом у него  контроля  нет.
 
      Диспетчер захвата может только удовлетворить или отвергнуть
 
      запрос на захват; он не может убедить процесс  пользоваться
 
      диспетчером захвата или считаться с наложенными  на  ресурс
 
      запретами.
 
           Для запроса доступа к ресурсу  используются  системные
 
      служебные программы SYS$ENQ или SYS$ENQW, ставящие  в  оче-
 
      редь запрос на захват [4]. SYS$ENQ ставит запрос в  очередь
 
      и возвращает управление; SYS$ENDQW ставит в очередь запрос,
 
      дожидается его удовлетворения и возвращает управление после
 
      этого. Процесс может показать, в каких пределах  он  желает
 
      разделить ресурс, с помощью шести режимов  захвата.  Напри-
 
      мер, нулевой захват позволяет любому другому процессу иметь
 


                                - 112 -
 
                          00152-01 97 09-1
 
 
      доступ к ресурсу на чтение и на  запись,  а  исключительный
 
      захват вообще не дает никакому другому процессу  доступа  к
 
      этому ресурсу.
 
           Обычно  при  вызове  служебной   системной   процедуры
 
      SYS$ENQW используются только следующие параметры:
 
           - режим захвата (параметр  2).задает  требуемый  режим
 
      захвата. Если разделяемый ресурс есть  терминал,  то  имеют
 
      смысл только два режима: исключительный  -  LCK$K_EXMODE  и
 
      нулевой - LCK$K_NLMODE;
 
           - блок состояния (параметр 3). Занимает  два  слова  и
 
      длинное слово. Первое слово получает окончательное  состоя-
 
      ние запроса на захват, а второе слово  -  держатель  места.
 
      При первой ссылке на ресурс в  длинное  слово  записывается
 
      идентификационный номер для  данного  устройства.  С  этого
 
      момента для передачи диспетчеру захвата  идентификационного
 
      номера при ссылке на  ресурс  используется  блок  состояния
 
      захвата;
 
           - возможности (параметр 4). Задается маска с  установ-
 
      ленным битом LCK$V_CONVERT для индикации того, что  диспет-
 
      чер захвата должен изменить режим захвата;
 
           - имя ресурса (параметр 5). Задается имя ресурса.
 
 
           Пример.
 
           Сегмент программы требует  нулевой  захват  ресурса  с
 
      именем TERMINAL. После удовлетворения этого  запроса  прог-
 
      рамма запрашивает изменение режима захвата на  исключитель-
 
      ный. Заметим, что после возврата из SYS$ENQW программа ана-
 
      лизирует состояние системной служебной процедуры и  состоя-
 
      ние, возвращенное в блок состояния  захвата,  для  гарантии
 
      успешного завершения запроса (идентификаторы режима захвата
 


                                - 113 -
 
                          00152-01 97 09-1
 
 
      определены в модуле $LCKDEF системной библиотеки макроопре-
 
      делений):
 
      !Определить режимы захвата
 
      INCLUDE '($LCKDEF)'
 
      !Определить блок состояния захвата
 
      STRUCTURE /STATUS_BLOCK/
 
      INTEGER*2 LOCK_STATUS,
 
      2         NULL
 
      INTEGER*4 LOCK_ID
 
      END STRUCTURE
 
      RECORD /STATUS_BLOCK/ IOSTATUS
 
      !
 
      ! продолжение описаний
 
      !
 
      !Определить режимы захвата
 
      STATUS = SYS$ENQW (,
 
      2                %VAL(LCK$K_NLMODE),
 
      2                IOSTATUS,
 
      2                ,
 
      2                'TERMINAL',
 
      2                ,,,,,)
 
      IF (NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF (.NOT. IOSTATUS.LOCK_STATUS)
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS.LOCK_STATUS))
 
      !Изменение режима захвата на исключительный
 
      STATUS = SYS$ENQW (,
 
      2           %VAL(LCK$K_EXMODE),
 
      2           IOSTATUS,
 
      2           %VAL(LCK$M_CONVERT),
 
      2           'TERMINAL',


                                - 114 -
 
                          00152-01 97 09-1
 
 
      2          ,,,,,)
 
      IF (.NOT. STATUS CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF (.NOT.IOSTATUS.LOCK_STATUS)
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS,LOSK_STATUS))
 
           Для разделения терминала между родительским  процессом
 
      и подпроцессом каждый процесс  выставляет  запрос  нулевого
 
      захвата на имя разделяемого  ресурса.  После  этого,  когда
 
      один из  процессов  желает  произвести  терминальный  ввод-
 
      вывод,  он  выставляет  запрос  на  исключительный  захват,
 
      выполняет ввод-вывод и запрашивает нулевой захват.
 
           Так как диспетчер захвата эффекктивен только при рабо-
 
      те взаимосвязанных программ, программа,  создавшая  подпро-
 
      цесс не должна заканчивать работу до конца  работы  подпро-
 
      цесса. Для гарантии того, что породивший процесс не  закон-
 
      чит работу до окончания работы подпроцесса,  задается  флаг
 
      события, который должен быть установлен по окончании работы
 
      подпроцесса (аргумент LIB$SPAWN "флаг  завершения").  Перед
 
      выходом из породившей программы для проверки того, что этот
 
      флаг установлен, используется  SYS$WAITER.  Для  подавления
 
      выходного сообщения подпроцесса можно с  помощью  служебной
 
      системной  процедуры  SYS$DELPRC  удалить  подпроцесс,   не
 
      допуская его нормального завершения.
 
           После окончания породившего процесса созданный подпро-
 
      цесс не может синхронизировать доступ  к  терминалу  и  для
 
      вывода на терминал должен использовать системную  служебную
 
      процедуру SYS$BRKTHRU.
 


                                - 115 -
 
                          00152-01 97 09-1
 
 
           3.1.3. Создание отсоединенного процесса
 
 
           Отсоединенный процесс следует  создавать  лишь  тогда,
 
      когда есть программа, которая должна продолжать работу пос-
 
      ле выхода породившего процесса из системы.
 
           Обычно при вызове SYS$CREPRC задается только следующее
 
      подмножество параметров:
 
           1) образ (параметр 2). Задается  имя  образа,  который
 
      должен выполняться созданным процессом. Этот параметр  обя-
 
      зателен.
 
           Созданный процесс не есть процесс, основанный на  DCL,
 
      поэтому команд DCL он не понимает.  Для  выполнения  команд
 
      DCL из процесса, созданного с помощью SYS$CREPRC, нужно:
 
           - задать в качестве образа для выполнения  SYS$SYSTEM:
 
      LOGINOUT.EXE;
 
           - приравнять SYS$INPUT к командной процедуре, содержа-
 
      щей команды, которые желательно выполнить;
 
           2) SYS$INPUT и SYS$OUTPUT (параметры 3  и  4).  Задают
 
      эквивалентные имена для  SYS$INPUT  и  SYS$OUTPUT.  Перевод
 
      логических имен не выполняется. Если эти параметры опущены,
 
      то SYS$INPUT и SYS$OUTPUT не определены;
 
           3) имя процесса (параметр 8). Задает имя для создавше-
 
      го процесса;
 
           4) приоритет (параметр 9). Задает для созданного  про-
 
      цесса базовый приоритет. Так  как  по  умолчанию  приоритет
 
      устанавливается 0, необходимо задать этот параметр. Если не
 
      требуется работать в  реальном  времени,  задается  приори-
 
      тет 4;
 
           5) возможности (параметр 12). Дает возможность указать
 
      характеристики созданного процесса. Например, указать,  что
 


                                - 116 -
 
                          00152-01 97 09-1
 
 
      процесс отсоединенный.
 
           Примеры:
 
           1. Программный сегмент создает процесс, который выпол-
 
      няет образ SYS$USER:[ACCOUNT]INCTAXES.EXE. INCTAXES  читает
 
      входные данные из TAXES и записывает выходные данные в файл
 
      TAXES.RPT (оба файла TAXES.DAT и таXES.RPT находятся в при-
 
      нятом по умолчанию каталоге на принятом по  умолчанию  дис-
 
      ке). Последний параметр указывает,  что  созданный  процесс
 
      является отсоединенным процессом (UIC по умолчанию устанав-
 
      ливается такой же, как у породившего процесса). Идентифика-
 
      тор PRC$M_DETACH определен в модуле $PRCDEF системной  биб-
 
      лиотеки макроопределений:
 
      EXTERNAL PRC$M_DETACH
 
      !Описать состояние и системные процедуры
 
      INTEGER STATUS,SYS$CREPRC
 
      !
 
      ! Продолжение описаний
 
      !
 
      STATUS = SYS$CREPRC (,
 
      2                     'SYS$USER:[ACCOUNT]INCTAXES',!Образ
 
      2                     'TAXES.DAT',             !SYS$INPUT
 
      2                     'TAXES.RPT',             !SYS$OUTPUT
 
      2                     ,,,,
 
      2                     %VAL(4),                 !Приоритет
 
      2                     ,,
 
      2                     %VAL(%LOC(PRC$M_DETACH)) !Отсоединен.
 
           2. Программный сегмент создает  отсоединенный  процесс
 
      для      исполнения      команд      DCL      из      файла
 
      SYS$USER[TEST]COMMANDS.COM.         Системный         образ
 
      SYS$SYSTEM:LOGINOUT.EXE выполняется  для  включения  DCL  в


                                - 117 -
 
                          00152-01 97 09-1
 
 
      созданный процесс. Команды DCL, предназначенные к  выполне-
 
      нию,  заданы  в   командной   процедуре,   передаваемой   в
 
      SYS$CREPRC в качестве входного файла. Выходные данные запи-
 
      сываются в файл SYS$USER:[TEST]OUTPUT.DAT:
 
      STATUS = SYS$CREPRC(,
 
      2                 'SYS$SYSTEM:LOGINOUT',       !Образ
 
      2                 'SYS$USER:[TEST]COMMANDS.COM,!SYS$INPUT
 
      2                 'SYS$USER:[TEST]OUTPUT.DATA',!SYS$OUTPUT
 
      2                 ,,,,
 
      2                 %VAL(4),                     !Приоритет
 
      2                 ,,
 
      2                 %VAL(%LOC(PRC$M_DETACH)) !Отсоединенный
 
           Чтобы из отсоединенного процесса выводить сообщения на
 
      терминал другого процесса, используется системная служебная
 
      процедура SYS$BRKTHRU.
 
 
 
           3.1.4. Выполнение программ по времени
 
 
           Для выполнения программы в заданное  время  или  через
 
      заданные интервалы времени создается подпроцесс или  отсое-
 
      диненный процесс, пробуждаемый в  нужные  моменты  времени.
 
      Подпроцесс удаляется, как только породивший процесс  закан-
 
      чивает работу, поэтому, если породивший процесс  может  или
 
      должен завершить работу до окончания выполнения программы в
 
      подпроцессе или отсоединенном процессе, то  вместо  подпро-
 
      цесса создается отсоединенный процесс.
 


                                - 118 -
 
                          00152-01 97 09-1
 
 
           3.1.4.1. Запуск в заданное время
 
 
           Для выполнения программы в заданное время создается  с
 
      помощью LIB$SPAWN процесс, выполняющий командную  процедуру
 
      из двух команд: команды WAIT языка  DCL  и  команды  вызова
 
      желаемой программы.  Если  нежелательно,  чтобы  породивший
 
      процесс оставался в спячке до завершения созданного процес-
 
      са, используется параллельное выполнение.
 
           Для  выполнения  программы  в  заданное  время   можно
 
      использовать также процедуру SYS$CREPRC. Процесс, созданный
 
      SYS$CREPRC,  после  выполнения  программы  не  прекращается
 
      (погружается в спячку), поэтому LIB$SPAWN предпочтительнее,
 
      если только не нужен отсоединенный процесс.
 
 
           Пример.
 
           Выполнение  проограммы  в  заданный  момент   времени.
 
      Порождающая программа запрашивает у  пользователя  интервал
 
      времени, присваивает значение этого интервала идентификато-
 
      ру EXECUTE_TIME и создает подпроцесс для выполнения команд-
 
      ной  процедуры  LATER.COM.  Kомандная  процедура  LATER.COM
 
      использует EXECUTE_TIME как параметр  команды  WAIT  (можно
 
      также разрешить пользователю ввести абсолютное  время  и  в
 
      своей программе заменить его интевалом времени, вычтя теку-
 
      щее время из заданного):
 
      !Временной интервал
 
      CHARACTER*17 TIME
 
      INTEGER LEN
 
      !Маска для LIB$SPAWN;
 
      INTEGER*4 MASK
 
      !Описать состояние и библиотечную процедуру
 
      INTEGER STATUS, LIB$SPAWN


                                - 119 -
 
                          00152-01 97 09-1
 
 
      !Принять временной интервал
 
      STATUS = LIB$GET_INPUT (TIME,
 
      2                       'TIME (DеLTA):',
 
      2                       LEN)
 
      IF(.NOT.STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !Приравнять идентификатор к времени
 
      STATUS = LIB$SET_SYMBOL ('EXECUTE_TIME',
 
      2                        TIME(1:LEN))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !Установить маску и вызвать LIB$SPAWN
 
      MASK = IBSET(MASK,0)  !Параллельное выполнение
 
      STATUS = LIB$SPAWN('CLATER'.
 
      2                  'DATA84.IN',
 
      2                  'DATA84.RPT', !Параллельно
 
      2                   MASK)
 
      END
 
 
 
      Файл LATER.COM имеет вид:
 
 
 
      $WAIT 'EXECUTE_TIME'
 
      $RUN SYS$DRIVE:[USER.MATH]CALC
 
      $DELETE/SYMBOL EXECUTE_TIME
 
 
 
           3.1.4.2. Запуск через интервалы времени
 
 
           Для выполнения программы через заданные интервалы вре-
 
      мени можно использовать как LIB$SPAWN, так и LIB$CREPRC.  С
 
      помощью LIB$SPAWN создается процесс, выполняющий  командную
 
      процедуру, содержащую три команды: команду WAIT языка  DCL,
 
      команду вызова желаемой программы и команду GOTO,  передаю-
 


                                - 120 -
 
                          00152-01 97 09-1
 
 
      щую управление команде WAIT. Если  нежелательно,  чтобы  до
 
      завершения подпроцесса породивший процесс оставался в спяч-
 
      ке, организуется параллельное выполнение.
 
           Для создания отсоединенного процесса  нужно  использо-
 
      вать процедуру SYS$CREPRC следующим образом:
 
           1) создать процесс и погрузить его в спячку. С помощью
 
      SYS$CREPRC создается процесс, выполняющий желательную прог-
 
      рамму. В параметре STSFLG устанавливается бит  PRC$V_HIBER,
 
      показывающий, что до выполнения  программы  процесс  должен
 
      находиться в спячке;
 
           2) спланировать для созданного  процесса  пробуждающие
 
      вызовы.   Используется   системная   служебная    программA
 
      SYS$SCHDWK для задания времени, в  которое  система  должна
 
      пробудить подпроцесс, и интервала  времени,  через  который
 
      это пробуждение следует повторить.
 
 
           Пример.
 
           Выполнение программы через заданные интервалы времени.
 
      Программа создает подпроцесс, который немедленно погружает-
 
      ся в спячку. Идентификационный номер созданного подпроцесса
 
      возвращается породившему процессу так, что  он  может  быть
 
      передан в SYS$SCHDWK. Система пробуждает подпроцесс в  6.00
 
      утра 23-го числа (год и месяц - те, которые приняты в  сис-
 
      теме по умолчанию), а потом - каждые десять минут:
 
      !Возможности и значения SYS$CREPRC
 
      INTEGER OPTIONS
 
      EXTERNAL PRC$V_HIBER
 
      !ID созданного подпроцесса
 
      INTEGER CR_ID
 
      !Двоичное время
 


                                - 121 -
 
                          00152-01 97 09-1
 
 
      INTEGER TIME(2),
 
      2       INTERVAL(2)
 
      !Установить... Бит в маске...
 
      !и создать процесс
 
      OPTIONS = IBSET (OPTIONS, %LOC(PRC$V_HIBER))
 
      STATUS = SYS$CREPRC(CR_ID,         !PID созданного процесса
 
      2                   'CHECK',       !Образ
 
      2                   ,,,,,
 
      2                   'SLEEP',       !Имя процесса
 
      2                   %VAL(4),       !Приоритет
 
      2                   ,,
 
      2                   %VAL(OPTIONS)) !Погрузить в спячку
 
      IF (.NOT.STATUS)CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Перевести абсолютное время в двоичное
 
      STATUS=SYS$BINTIM ('23-- 06:00.00',!6:00 A.T.
 
      2                  TIME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Интервал в двоичное
 
      STATUS = SYS$BINTIM ('0 :10:00.00',   ! 10 минут
 
      2                     INTERVAL)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Планировка времени пробуждения
 
      STATUS = SYS$SCHDWK (CR_ID,  !ID созданного процесса
 
      2                    ,
 
      2                    TIME,   !Начальное время пробуждения
 
      2                    INTERVAL)!Время повторного пробуждения
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 


                                - 122 -
 
                          00152-01 97 09-1
 
 
           3.2. Слежение за процессами
 
 
           Для создания процессов , их  модификации  и  получения
 
      информации о них существуют следующие процедуры:
 
           LIB$GETJPI - возвращает информацию о процессе;
 
           SYS$GETJPI - возвращает информацию о процессе;
 
           SYS$GETJPIW - возвращает информацию о процессе;
 
           SYS$SETPRN - устанавливает имя процесса;
 
           SYS$SETPRI - устанавливает приоритет процесса;
 
           SYS$SETSWM - управляет обменом рабочих наборов.
 
           По умолчанию эти процедуры обращаются к текущему  про-
 
      цессу. Для ссылки на другой процесс следует задать либо его
 
      идентификационный номер, либо имя при вызове процедуры. Для
 
      ссылки на процесс с тем же номером группы и другим  номером
 
      члена в коде идентификации  пользователя  необходимо  иметь
 
      привилегию GROUP; для ссылки  на  процесс  с  иным  номером
 
      группы в  коде  идентификации  пользователя  (UIC)  следует
 
      иметь привилегию WORLD.
 
 
 
           3.2.1. Получение информации о процессе
 
 
           Обычно для получения информации о процессе использова-
 
      ния процедура LIB$GETJPI из библиотеки исполнительных  про-
 
      цедур [2]. Процедуры LIB$GETJPI, SYS$GETJPI  и  SYS$GETJPIW
 
      разделяют один и тот же список об'ектов за  одним  исключе-
 
      нием:  об'екты  LIB$K_  могут  быть  доступы   только   для
 
      LIB$GETJPI.
 
           Заметим, что некоторые из  об'ектов,  перечисленных  в
 
      описании LIB$GETJPI, могут быть возвращены в  виде  длинных
 
      слов или строк. SYS$GETJPI  и  SYS$GETJPIW  возвращают  эти
 
      об'екты лишь в виде длиннословных значений.


                                - 123 -
 
                          00152-01 97 09-1
 
 
           Примеры:
 
           1. Вместо числового аргумента можно указать  аргумент-
 
      строку, и LIB$GETJPI возвратит код идентификации  пользова-
 
      теля процесса в виде строки:
 
      !Определить коды запроса
 
      INCLUDE '($JPIDEF)'
 
      !Переменные для LIB$GETJPI
 
      CHARACTER*9 UIC
 
      INTEGER LEN
 
      STATUS = LIB$GETJPI (JPI$_UIC,
 
      2                   ,,,
 
      2                   UIC,
 
      2                   LEN)
 
           Если нужно получить ту же информацию о каждом процессе
 
      в системе, достаточно задать при вызове LIB$GETJPI  иденти-
 
      фикационный  параметр  процесса   как   -   1.   Вызывается
 
      LIB$GETJPI  до  тех   пор,   пока   она   не   вернет   код
 
      SS$_NOMOREPROC, показывающий, что опрошены все  процессы  в
 
      системе.
 
           2. Программа  создает  файл  PROCNAME.RPT,  включающий
 
      имена всех процессов в системе. Если процесс, под  управле-
 
      нием которого выполняется данная программа, не имеет приви-
 
      легии, достаточной для  доступа  к  некоторому  конкретному
 
      процессу, то вместо имени этого процесса программа  запишет
 
      слова  NO  PRIVILEGE.  Если   процесс   приостановлен,   то
 
      LIB$GETJPI не имеет к нему доступа; вместо его имени  будет
 
      записано слово SUSPENDED. Нужно заметить, что в  каждом  из
 
      этих случаев  значение  переменной  STATUS,  сообщающее  об
 
      ошибке, заменяется на значение  успешного  завершения,  так
 
      что цикл, вызывающий LIB$GETJPI, продолжает выполняться:


                                - 124 -
 
                          00152-01 97 09-1
 
 
      !Переменная состояния и коды ошибок
 
      INTEGER STATUS,
 
      2       STATUS_OK,
 
      2       LIB$GET_LUN,
 
      2       LIB$GETJPI
 
      INCLUDE '($SSDEF)'
 
      PARAMETR (STATUS_OK = 1)
 
      !Номер логического устройства и имя файла
 
      INTEGER*4 LUN
 
      CHARACTER*(*) FILE_MAME
 
      PARAMETER (FILE_NAME = 'PROCNAME.RPT')
 
      !Определение кодов об'ектов для LIB$GETJPI
 
      INCLUDE '($JPIDEF)'
 
      !Имя процесса
 
      CHARACTER*15 NAME
 
      INTEGER LEN
 
      !Идентификация процесса
 
      INTEGER PID /-1/
 
      !Принять логический номер устройства и открыть файл
 
      STATUS = LIB$GET_LUN (LUN)
 
      OPEN (UNIT = LUN,
 
      2      FILE = 'PROCNAME.RPT',
 
      2      STATUS = (NEW')
 
      !Принять информацию и записать ее в файл
 
      DO WHILE (STATUS)
 
      STATUS = LIB$GETJPI(JPI$_PRCNAM,
 
      2                   PID,
 
      2                   ,,
 
      2                   NAME,
 
      2                   LEN)


                                - 125 -
 
                          00152-01 97 09-1
 
 
      !Дополнительное место в команде WRITE предназначено
 
      !для фортранного управления кареткой
 
      WRITE (UNIT = LUN,
 
      2      FMT = '(2A)')' ',NAME(1:LEN)
 
      STATUS = STATUS_OK
 
      ELSE IF (STATUS.EQ.SS$_NOPRIV) THEN
 
      WRITE (UNIT = LUN,
 
      2       FMT = '(2A)')' ', 'NO PRIVILEGE'
 
      STATUS = STATUS_OK
 
      ELSE IF (STATUS.EQ. SS$_SUSPENDED) THEN
 
      WRITE (UNIT = LUN,
 
             FMT = '(2A)')' ', 'SUSPENDED'
 
      STATUS = STATUS_OK
 
      END IF
 
      END DO
 
      !Закрыть файл
 
      IF(STATUS.EQ. SS$_NOMOREPROC)
 
      2 CLOSE (UNIT = LUN)
 
           LIB$CETJPI  предоставляет  простой  способ   получения
 
      информации по какому-то одному пункту. Для получения инфор-
 
      мации о процессе по многим пунктам можно сделать многократ-
 
      ные  вызовы  LIB$GETJPI  или  использовать  SYS$GETJPI  или
 
      SYS$GETJPIW.   SYS$GETJPI   выполняется    асинхронно,    а
 
      SYS$GETJPIW выполняется синхронно, во  всем  остальном  эти
 
      процедуры совпадают.
 
           3. Использование структур записей для  задания  списка
 
      об'ектов SYS$GETJPI  и  SYS$GETJPIW.  Программа  с  помощью
 
      SYS$GETJPIW запрашивает имя процесса и имя  его  владельца;
 
      идентификационный    номер    процесса     содержится     в
 
      SUBPROCESS.FID:


                                - 126 -
 
                          00152-01 97 09-1
 
 
      !PID подпроцесса
 
      INTEGER SUBPROCESS_PID
 
      !Включить коды запроса
 
      INCLUDE '($JPIDEF)'
 
      !Определить структуру для списка об'ектов
 
      STRUCTURE /ITMLST/
 
      UNION
 
      MAP
 
      INTEGER*2 BUFLEN
 
      INTEGER*2 CODE
 
      INTEGER*4 BUFADR
 
      INTEGER*4 RETLENADR
 
      END MAP
 
      MAP
 
      INTEGER*4 END_LIST
 
      END MAP
 
      END UNION
 
      END STRUCTURE
 
      !Определить список об'ектов
 
      RECORD /ITMLST/JPI_LIST(3)
 
      !Описать буфера для информации
 
      CHARACTER*15 PROCESS_NAME
 
      CHARACTER*12 USER_NAME
 
      INTEGER*4    PNAME_LEN,
 
      2            UNAME_LEN
 
      !Описать структуру состояния ввода-вывода
 
      STRUCTURE /IOSB/
 
      INTEGER*2 STATUS,
 
      2          COUNT
 
      INTEGER*4  %FILL


                                - 127 -
 
                          00152-01 97 09-1
 
 
      END STRUCTURE
 
      !Описать переменную состояния ввода-вывода
 
      RECORD /IOSB/ JPISTAT
 
      !Описать состояние и процедуру
 
      INTEGER*4   STATUS,
 
      2           SYS$GETJPIW
 
      !
 
      ! Определить SUBPROCESS_PID
 
      !
 
      !Установка полей переменной записи
 
      JPI_LIST(1) BUFLEN  = 15
 
      JPI_LIST(1)CODE     = JPI$_PROCNAM
 
      JPI_LIST(1).BUFADR  = %LOC(PROCESS_NAME)
 
      JPI_LIST(1).RETLENADR = %LOC(PNAME_LEN)
 
      JPI_LIST(2).BUFLEN    = 12
 
      JPI_LIST(2) CODE      = JPI$_USERNAME
 
      JPI_LIST(2).BUFADR = %LOC(USER_NAME)
 
      JPI_LIST(2).RETLENADR = %LOC(UNAME_LEN)
 
      JPI_LIST(3).END_LIST = 0
 
      !Запросить информацию и ждать ее
 
      STATUS = SYS$GETJPIW(,
 
      2                   SUBPROCESS_PID,
 
      2                   ,
 
      2                   JPI_LIST,
 
      2                   JPISTAT,
 
      2                   ,)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Проверить состояние окончательного возврата
 
      IF (.NOT.JPISTAT.STATUS) THEN
 
      CALL LIB$SIGNAL (%VAL(JPISTAT.STATUS))


                                - 128 -
 
                          00152-01 97 09-1
 
 
      END IF
 
 
 
           3.2.2. Слежение за выполнением процесса
 
 
           При выполнении длинного образа слежение за его  выпол-
 
      нением довольно трудно, особенно если этот образ не выводит
 
      информацию на терминал. Использование  системной  служебной
 
      процедуры SYS$SETPRN для изменения имени процесса в узловых
 
      точках программы позволяет следить за  ее  работой  либо  с
 
      помощью <CTRL/T> (если в данный момент образ выполняется  в
 
      текущем процессе), либо с помощью команды DCL  SHOW  SYSTEM
 
      (если образ выполняется в  отсоединенном  процессе,  таком,
 
      как пакетное задание).
 
 
           Пример.
 
           Сегмент программы подсчитывает  налог  для  некоторого
 
      количества домохозяйств, упорядочивает их согласно  налого-
 
      вому статусу и записывает результаты в  файл  отчета.  Пос-
 
      кольку этот процесс времяемкий, программа изменяет имя про-
 
      цесса в узловых точках, так что за ее работой можно просле-
 
      дить:
 
      !Приближенный расчет размеров налога
 
      STATUS = SYS$SFTPRN ('INCTAXES')
 
      IF(.NOT.STATUS) CALL LIB$SIGNAL(STATUS))
 
      STATUS = TAX_RATES (TOTAL_HOUSES,
 
      2                   PERSONS_HOUSE,
 
      2                   ADULTS_HOUSE,
 
      2                   INCOME_HOUSE,
 
      2                   TAX_PER_HOUSE)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      !Сортировать


                                - 129 -
 
                          00152-01 97 09-1
 
 
      STATUS = SYS$SETPRN ('INCSORT')
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      STATUS = TAX_SORT (TOTAL_HOUSES,
 
      2                  TAX_PER_HOUSE)
 
      IF (.NOT.STATUS)CALL LIB$SIGNAL(%VAL(STATUS))
 
      !Писать отчет
 
      STATUS = SYS$SETPRN ('INCREPORT')
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
 
 
           3.2.3. Управление планированием процесса
 
 
           В общем случае управление планированием процесса  сле-
 
      дует предоставить системе. Однако существуют две возможнос-
 
      ти планирования, предоставленные пользователю:
 
           1) обмен рабочих наборов. Запрет обмена рабочих  набо-
 
      ров сохраняет процесс в физической памяти. Запрет  рекомен-
 
      дуется только в тех случаях, когда от этого зависит  эффек-
 
      тивное выполнение образа (например,  образ,  выполняемый  в
 
      процессE, следит за работой процессора).
 
           Запрет обмена  рабочих  наборов  для  любого  процесса
 
      производится с помощью системной процедуры SYS$SETSWM. Если
 
      создается подпроцесс  с  помощью  процедуры  LIB$SPAWN,  то
 
      обмен рабочих наборов можно запретить с помощью команды DCL
 
      SET PROCESS/NOSWAP, указав ее первой в командной процедуре.
 
      Если процесс создается с помощью системной служебной проце-
 
      дуры SYS$CREPRC, TO обмен рабочих наборов можно  запретить,
 
      установив бит запрета в параметре STSFLG. Для запрета обме-
 
      на рабочих  наборов  процесс  должен  обладать  привилегией
 
      PSWAPM;
 
           2) приоритет. Увеличение базового приоритета  процесса
 


                                - 130 -
 
                          00152-01 97 09-1
 
 
      дает процессу больше процессорного времени за счет  процес-
 
      сов, выполняемых с низшими приоритетами.  Этого  делать  не
 
      рекомендуется, за исключением тех случаев, когда  программа
 
      должна немедленно реагировать на какие-либо события (напри-
 
      мер, программа реального времени). Если  пришлось  повысить
 
      базовый приоритет, то необходимо вернуть его к  нормальному
 
      при первой возможности. Если весь образ должен  выполняться
 
      при повышенном приоритете, то перед  окончанием  выполнения
 
      образа следует вернуться к базовому приоритету (прекращение
 
      работы образа не восстанавливает базовый приоритет).
 
           Базовый приоритет любого процесса устанавливается сис-
 
      темной  служебной  процедурой  SYS$SETPRI.  Eсли  создается
 
      подпроцесс с  помощью  процедуры  LIB$SPAWN,  то  приоритет
 
      подпроцесса   можно    установить    командой    DCL    SET
 
      PROCESS/PRIORITY, поставив эту команду первой  в  командной
 
      процедуре. Если создается процесс с помощью системной  слу-
 
      жебной процедуры SYS$CREPRC,  то  базовый  приоритет  можно
 
      установить с помощью параметра BASPRI. Для повышения  базо-
 
      вого приоритета требуется привилегия ALTPRI.
 
 
 
           3.3. Управление выполнением программы
 
 
           Для управления выполнением  программ  и  синхронизации
 
      операций служат флаги событий и асинхронные системные  пре-
 
      рывания.
 


                                - 131 -
 
                          00152-01 97 09-1
 
 
           3.3.1. Синхронизация с помощью флагов событий
 
 
           Флаги событий, как показано  в  табл.  5,  делятся  на
 
      четыре кластера: два - для флагов локальных событий и два -
 
      для общих флагов событий.
 
                                                        Таблица 5
 
                          Флаги событий
 
      -----------------------------------------------------------
               !              !
       Кластер !    Флаги     !           Назначение
               !              !
      -----------------------------------------------------------
               !
         0     ! от 0 до 31   !   Локальные события. Флаги от 24
               !
               !              ! до 31 резервируются для системно-
               !
               !              ! го применения
               !
         1     ! от 32 до 63  !   Локальные события
               !
         2     ! от 64 до 95  !   Общие события
               !
         3     ! от 96 до 127 !   То же
               !
 
           Флаги локальных событий специфичны  для  процесса;  их
 
      применяют для синхронизации событий внутри одной  программы
 
      или для передачи информации от образа, выполняемого в  нас-
 
      тоящий момент, образу, который будет выполняться в  том  же
 
      самом процессе позже. Флаги локальных событий автоматически
 
      доступны каждой программе. Автоматическая их  инициализация
 
      не производится, но если флаг передается системной  служеб-
 
      ной процедуре, например, SYS$GETJPI, эта процедура  инициа-
 
      лизирует флаг перед его использованием.
 
           Общие флаги событий специфичны для группы; их  исполь-
 
      зуют для синхронизации событий в  образах,  которые  выпол-
 
      няются в различных процессах, если обеспечено, что эти про-
 
      цессы находятся в одной группе. Программа перед ссылкой  на
 
      общий флаг события должна связать имя кластера с  кластером


                                - 132 -
 
                          00152-01 97 09-1
 
 
      этого общего флага события.
 
 
 
           3.3.1.1. Флаги локальных событий
 
 
           Системно-определенные процедуры, приведенные в  табл.6
 
      дают возможность работать с флагами событий. Четыре послед-
 
      них процедуры из этого списка переводят процесс в  ожидание
 
      флага локальных события  (LEF)  или  общего  флага  события
 
      (CEF), в зависимости от заданного флага (флагов).
 
                                                        Таблица 6
 
                Процедуры обработки флагов событий
 
      -----------------------------------------------------------
                   !
        Процедура  !             Назначение
                   !
      -----------------------------------------------------------
                   !
       LIB$GET_EF  !   Возвращает номер флага локального собы-
                   !
                   ! тия не используемого в настоящий момент
                   !
       LIB$FREE_EF !   Освобождает номер флага локального события
                   !
       SYS$CLREF   !   Сбрасывает флаг события
                   !
       SYS$SETEF   !   Устанавливает флаг события
                   !
       SYS$READEF  !   Возвращает значение одного кластера
                   !
       SYS$SYNCH   !   Ждет флага события и ненулевого блока
                   !
                   ! состояния
                   !
       SYS$WAITFR  !   Ждет установки флага события
                   !
       SYS$WFLOR   !   Ждет установки одного из нескольких флагов
                   !
                   ! событий
                   !
       SYS$WFLAND  !   Ждет установки всех флагов событий
                   !
 
           Для ссылки на флаг  события  пользуются  его  номером,
 
      входящим в один из соответствующих кластеров. Например, для
 
      ссылки на флаг  локального  события  можно  воспользоваться
 
      любым номером от 0 до 63 (исключая номера от 24 до 31). Для


                                - 133 -
 
                          00152-01 97 09-1
 
 
      предотвращения случайного  повторного  использования  флага
 
      события, уже использованного где-то в программе, пользуются
 
      процедурами   из   библиотеки    исполнительных    процедур
 
      LIB$GET_IF и LIB$FREE_EF.  Процедура  LIB$GET_EF  возращает
 
      номер  одного  из  свободных   флагов   событий   процедура
 
      LIB$EREE_EF возвращает  указанный  номер  флага  локального
 
      события  в  список  свободных.   Процедуры   LIB$GET_EF   и
 
      LIB$FREE_EF могут отслеживать только те флаги, которые  они
 
      занимают и освобождают; чтобы они работали эффективно, нуж-
 
      но применять их в программе повсеместно. Для  общих  флагов
 
      событий таких процедур не существует.
 
           Пример.
 
           Флаг   локального   события   выбирается   с   помощью
 
      LIB$GET_EF,A с помощью  SYS$CLREF  устанавливается  в  ноль
 
      (сброс флага события). В библиотечные процедуры  библиотеки
 
      исполнительных процедур номер флага события  передается  по
 
      ссылке, а системные служебные  процедуры  требуют  передачи
 
      номера флага события по значению:
 
      INTEGER FLAG,
 
      2       STATUS,
 
      2       LIB$GET_EF,
 
      2       SYS$CLREF
 
      STATUS = LIB$GET_EF (FLAG)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$CLREF (%VAL(FLAG))
 
      IF (.NOT.STATUS ) CALL LIB$SIGNAL (%VAL(STATUS))
 


                                - 134 -
 
                          00152-01 97 09-1
 
 
           3.3.1.2. Общие флаги событий
 
 
           С общими флагами событий работа идет так же, как  и  с
 
      флагами локальных событий.  Однако  кластеры  общих  флагов
 
      событий не присоединяются к программе автоматически.  Перед
 
      ссылкой на общий  флаг  события  программа  должна  создать
 
      кластер общих флагов событий путем связывания его с  каким-
 
      нибудь именем. После того,  как  такая  связь  установлена,
 
      программа может ссылаться на любой флаг кластера.
 
           Для связывания имени с кластером общих флагов  событий
 
      используется системная служебная процедура SYS$ASCEFC. Пер-
 
      вая программа, давшая кластеру имя, его создает; все  флаги
 
      в созданном кластере будут  сброшены.  Другие  процессы,  у
 
      которых номер  группы  в  коде  идентификации  пользователя
 
      (UIC) совпадает с номером группы процесса, создавшего клас-
 
      тер, могут ссылаться на этот кластер с помощью  SYS$ASCEFC,
 
      задавая то же имя.
 
           Различные процессы могут связывать различные  кластеры
 
      общих флагов событий с одним и тем же именем; до  тех  пор,
 
      пока имя то же самое, процессы ссылаются на один и  тот  же
 
      кластер. Внутри кластера для ссылки на бит используется  не
 
      номер бита, а смещение. Например, первый  программный  сег-
 
      мент связывает кластер общих  флагов  событий  3  с  именем
 
      CLUSTER, а потом сбрасывает  второй  флаг  события  в  этом
 
      кластере; вторая программа связывает кластер  общих  флагов
 
      событий 2 с именем CLUSTER и устанавливает в этом  кластере
 
      второй флаг (тот самый, что был сброшен первой программой):
 
      !Программа 1
 
      STATUS = SYS$ASCEFC (%VAL(96),
 
      2                   'CLUSTER',,)
 


                                - 135 -
 
                          00152-01 97 09-1
 
 
      STATUS = SYS$CLREF (%VAL(98))
 
      !Программа 2
 
      STATUS = SYS$ASCEFC (%VAL(64),
 
      2                   'CLUSTER',,)
 
      STATUS = SYS$SETEF(66))
 
           Рекомендуется номер флага события задавать символичес-
 
      кими именами:
 
           - одно имя указывает номер первого бита  используемого
 
      кластера (0,32,64,96);
 
           - второе имя указывает номер бита  в  клAстере  (от  0
 
      до 31).
 
           Для ссылки на  номер  флага  используется  сумма  этих
 
      имен.
 
 
           Пример.
 
           Задание флагов событий символическими именами:
 
      !Программа 1
 
      INTEGER*4 BASE,
 
      2         оFFSET
 
      PARAMETER (BASE = 96)
 
      OFFSET=2
 
      STATUS = SYS$ASCEFC (%VAL(BASE)
 
      2                    'CLUSTER',,)
 
      STATUS = SYS$CLREF (%VAL(BASE+OFFSET))
 
      !Программа 2
 
      INTEGER*4 BASE,
 
      2         OFFSET
 
      PARAMETER (BASE = 64)
 
      OFFSET = 2
 
      STATUS = SYS$ASCEFC (%VAL(BASE),
 


                                - 136 -
 
                          00152-01 97 09-1
 
 
      2                   'CLUSTER',,)
 
      STATUS = SYS$SETEF (%VAL(BASE+OFFSET))
 
 
           Общие флаги событий часто применяются для связи  между
 
      подпроцессом и создавшим его родительским процессом. Напри-
 
      мер, родительский процесс связывает имя CLUSTER с кластером
 
      общих флагов событий, создает подпроцесс и ждет, пока  этот
 
      подпроцесс установит флаг события 64:
 
 
      INTEGER*4 BASE,
 
      2         OFFSET
 
      PARAMETER (BASE = 64,
 
      2          OFFSET = 0)
 
      !Связать имя с кластером общих флагов событий
 
      STATUS = SYS$ASCEFC (%VAL(BASE),
 
      2                    'CLUSTER',,)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Создать подпроцесс для параллельного выполнения;
 
      MASK = IBSET (MASK,0)
 
      STATUS = LIB$SPAWN ('RUN REPORTSUB', !Образ
 
      2                    'INPUT.DAT',    !SYS$INPUT
 
      2                    'OUTPUT.DAT',   !SYS$OUTPUT
 
      2                    MASK)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Ждать ответа от подпроцесса
 
      STATUS = SYS$WAITFR (%VAL(BASE+OFFSET))
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
 
           Программа REPORTSUB, выполняющаяся в подпроцессе, свя-
 
      зывает имя CLUSTER с кластером общих флагов событий, выпол-
 
      няет некоторые  действия,  устанавливает  флаг  события  64
 


                                - 137 -
 
                          00152-01 97 09-1
 
 
      (разрешающий породившему процессу продолжать выполнение)  и
 
      продолжает работу:
 
 
      INTEGER*4 BASE,
 
      2         OFFSET
 
      PARAMETER (BASE = 64,
 
      2          OFFSET = 0
 
      !Действия для продолжения
 
      !родительского процесса:
 
      !Связать кластер общих флагов событий с  именем
 
      STATUS = SYS$ASCEFC (%VAL(BASE),
 
      2                     'CLUSTER',,)
 
      IF(.NOT.STATUS)
 
      2 CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Разрешить выполнение родительского процесса
 
      STATUS = SYS$SETEF (%VAL(BASE + OFFSET))
 
 
           По умолчанию связь между кластером общих флагов  собы-
 
      тий и именем кластера аннулируется, когда установивший  эту
 
      связь образ заканчивает работу; когда от кластера  отсоеди-
 
      няется последний связанный с ним образ, этот кластер  общих
 
      флагов событий уничтожается. Кластеры,  уничтожаемые  после
 
      отсединения от них всех связанных с ними образов, называют-
 
      ся временными кластерами.
 
           При наличии привилегии PRMCEB можно создать постоянный
 
      кластер флагов событий (при  вызове  SYS$ASCEFG  установить
 
      параметр PERM в единицу). Постоянный кластер флагов событий
 
      не будет удален до тех пор, пока не будет помечен на удале-
 
      ние системной служебной процедурой SYS$DLCEFC (требует при-
 
      вилегии PRMCEB). С того  момента,  как  постоянный  кластер
 
      помечен на удаление, он становится подобен временному; пос-


                                - 138 -
 
                          00152-01 97 09-1
 
 
      ле отсоединения последнего связанного  с  ним  образа  этот
 
      кластер удаляется.
 
 
 
           3.3.1.3. Синхронные и асинхронные служебные  системные
 
      процедуры
 
 
           Многие из системных служебных  процедур  могут  выпол-
 
      няться  как  синхронно,   так   и   асинхронно,   например,
 
      SYS$GETJPIW (асинхронное выполнение) и SYS$GETJPI  синхрон-
 
      ное. Буква "W" в конце имени системной служебной  процедуры
 
      означает синхронную версию этой процедуры.
 
           Асинхронная системная  служебная  процедура  ставит  в
 
      очередь запрос и возвращает управление вызвавшей программе.
 
      Можно продолжать работу  программы  параллельно  выполнению
 
      системной служебной процедуры, но невозможно воспользовать-
 
      ся информацией, которую эта процедура  должна  вернуть,  не
 
      проверив предварительно, что процедура свою работу закончи-
 
      ла.
 
           Обычно асинхронной системной служебной процедуре пере-
 
      дается флаг события и блок  состояния  ввода-вывода.  Когда
 
      системная  служебная  процедура   завершает   работу,   она
 
      устанaвливает флаг события  и  помещает  окончательный  код
 
      состояния запроса в блок состояния ввода-вывода.  Для  про-
 
      верки завершения  работы  можно  воспользоваться  системной
 
      служебной процедурой SYS$SYNCH. Процедуре  SYS$SYNCH  пере-
 
      дается флаг события и блок состояния ввода-вывода,  которые
 
      были переданы асинхронной  системной  служебной  процедуре;
 
      SYS$SYNCH ожидает установки флага события, анализируя  блок
 
      состояния ввода-вывода. Если  блок  состояния  ввода-вывода
 
      содержит ноль, то SYS$SYNCH ждет заполнения этого блока.
 


                                - 139 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
      !Структура данных для SYS$GETJPI
 
      INTEGER*4 STATUS,
 
      2          FLAG,
 
      2          PID_VALUE
 
      !Блок состояния ввода-вывода
 
      STRUCTURE /STATUS_BLOCK/
 
      INTEGER*2 JPISTATUS,
 
      2         LEN
 
      INTEGER*4 ZERO /0/
 
      END STRUCTURE
 
      RECORD /STATUS_BLOCK/ IOSTATUS
 
      !Вызвать SYS$GETJPI и ждать информации
 
      STATUS = LIB$GET_EF (FLAG)
 
      IF (.NOT.STATUS) CALL (%VAL(STATUS))
 
      STATUS = SYS$GETJPI (%VAL(FLAG),
 
      2                    PID_VALUE,
 
      2                    ,
 
      2                    NAME_BUF_LEN,
 
      2                    IOSTATUS,
 
      2                    ,)
 
      IF (.NOT.STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$SYNCH (%VAL(FLAG),
 
      2                   IOSTATUS)
 
      IF (.NOT.IOSTATUS.JPISTATUS) THEN
 
      CALL LIB$SIGNAL (%VAL(IOSTATUS.JPISTATUS))
 
      END IF
 
      END
 
           Синхронная версия служебной системной процедуры  рабо-
 
      тает в точности как асинхронная, за которой непосредственно


                                - 140 -
 
                          00152-01 97 09-1
 
 
      следует вызов SYS$SYNCH. Независимо от  того,  используется
 
      ли синхронная или  асинхроная  версия,  если  параметр  EFN
 
      будет опущен, процедура будет использовать флаг  события  0
 
      (по умолчанию).
 
 
 
           3.3.2. Использование      асинхронного      системного
 
      прерывания (AST)
 
 
           ФОРТРАН предоставляет возможность использования  меха-
 
      низма асинхронных системных прерываний  (AST)  в  пользова-
 
      тельских программах.
 
           Постановка в очередь на AST достигается указанием име-
 
      ни подпрограммы обработки системного прерывания в  качестве
 
      параметра ASTADR, передаваемого некоторой системной служеб-
 
      ной процедуре. Система доставляет  AST  (то  есть  передает
 
      управление указанной подпрограмме) в определенное время или
 
      в ответ на конкретное событие.
 
           В программе обработки AST должны соблюдаться ограниче-
 
      ния, связанные со следующими понятиями:
 
           1) параметры. Механизм постановки  в  очередь  AST  не
 
      обеспечивает  возвращения  значения  функции  или  передачи
 
      параметров. Поэтому программа  обработки  AST  должна  быть
 
      написана как подпрограмма, а для передачи параметров  между
 
      программой AST и программой, ставящей ее в очередь, следует
 
      использовать блоки общей памяти.
 
           В некоторых  случаях  системная  служебная  процедура,
 
      ставящая в очередь на AST, дает возможность задать параметр
 
      для программы обработки AST (например, SYS$GETJPI). Если  в
 
      программе должна использоваться эта возможность,  то  прог-
 
      рамма обработки AST должна  быть  написана  таким  образом,
 


                                - 141 -
 
                          00152-01 97 09-1
 
 
      чтобы она могла воспринять параметр;
 
           2) терминальный ввод-вывод. Попытка получить доступ  к
 
      терминалу с помощью  процедур  ввода-вывода  SYS$INPUT  или
 
      SYS$OUTPUT (например, задав UNIT  =  *)  может  привести  к
 
      ошибке ввода-вывода из-за избыточности. Необходимо  устано-
 
      вить другой канал к терминалу путем  явного  открытия  (или
 
      использования процедур SMG$  из  библиотеки  исполнительных
 
      процедур);
 
           3) разделяемые процедуры. Программа оработки AST может
 
      вызывать подпрограмму, которая вызывается  также  и  какой-
 
      нибудь другой программой  единицей  той  же  программы.  Во
 
      избежание  конфликтов  программная  единица  перед  вызовом
 
      подпрограммы, которая  может  быть  использована  одной  из
 
      программ обработки AST, должна обратиться к системной  слу-
 
      жебной процедуре SYS$SETAST для запрета асинхронных систем-
 
      ных прерываний. После выполнения  разделяемой  подпрограммы
 
      та же программная единица может снова разрешить асинхронные
 
      системные прерывания с помощью той же  системной  служебной
 
      поцедуры;
 
           4) вызов. Никогда не следует вызывать программу  обра-
 
      ботки AST непосредственно как подпрограмму или функцию;
 
           5) итерация. Никогда не следует разрешать  циклическую
 
      обработку AST. Системная служебная  процедура,  ставящая  в
 
      очередь программу обработки AST, определяет, доставлено  ли
 
      данное AST в заданное время или после заданного события;
 
           6) событие. Следующие  системные  служебные  процедуры
 
      разрешают задать  подпрограмму  обработки  AST,  получающей
 
      управление  после  их   выполнения:   LIB$SPAWN,   SYS$ENQ,
 
      SYS$ENQW, SYS$GETDVI, SYS$GETDVIW, SYS$GETJPI, SYS$GETJPIW,
 
      SYS$GETSYI, SYS$GETSYIW, SYS$QIO, SYS$QIOW, SYS$UPDSEC.


                                - 142 -
 
                          00152-01 97 09-1
 
 
           Системная служебная процедура SYS$SETPRA дает  возмож-
 
      ность задать подпрограмму обработки AST, получающую  управ-
 
      ление по восстановлению питания;
 
           7) время.  Системная  служебная  процедура  SYS$SETIMR
 
      дает возможность задать время инициации AST.
 
           Системная  служебная  процедура  SYS$DCLAST  позволяет
 
      инициализировать заданное  AST  немедленно.  Это  идеальный
 
      инструмент для отладки программ обработки AST.
 
           Если программа выставила запрос  на  AST  и  завершила
 
      работу до его наступления, то этот запрос удаляется из оче-
 
      реди. Если процесс в момент  наступления  AST  находится  в
 
      спячке,  то  программа  обработки  выполняется,  а  процесс
 
      остается в  спячке.  Если  процесс  находится  в  состоянии
 
      приостанова в момент наступления AST, то программа обработ-
 
      ки AST будет выполнена сразу же, как только  процесс  будет
 
      возобновлен. Если наступило более одного AST, то они  будут
 
      выполняться в порядке поступления.
 
           Обычно программы обработки AST используются  вместе  с
 
      SYS$QIO или SYS$QIOW для обслуживания <CTRL/C>, <CTRL/Y>  и
 
      незапланированного ввода.
 
 
 
           3.4. Обмен информацией между процессами
 
 
           Для передачи информации между образами, выполняющимися
 
      в разных процессах, используются идентификаторы, логические
 
      имена, почтовые ящики, установленные блоки общей  памяти  и
 
      глобальные секции. Как правило, с помощью  логических  имен
 
      передаются короткие сообщения от одного образа  к  другому.
 
      Почтовые ящики, установленные блоки общей памяти и глобадь-
 
      ные секции используются для ведения диалога между образами.
 


                                - 143 -
 
                          00152-01 97 09-1
 
 
      Чем длиннее сообщения в диалоге, тем целесообразнее исполь-
 
      зовать установленные блоки общей памяти или глобальные сек-
 
      ции.
 
 
 
           3.4.1. Идентификаторы и логические имена
 
 
           В табл. 7 перечислены системно-определенные процедуры,
 
      которые позволяют  создавать,  анализировать  и  уничтожить
 
      идентификаторы и логические имена.
 
                                                        Таблица 7
 
      -----------------------------------------------------------
                         !
         Процедура       !             Назначение
                         !
      ---------------------------------------------------------
                         !
       LIB$SET_SYMBOL    !   Определяет или переопределяет
                         !
                         !   идентификатор
                         !
       LIB$GET_SYMBOL    !   Возвращает значение идентификатора
                         !
       LIB$DELETE_SYMBOL !   Уничтожает идентификатор
                         !
       SYS$CRELNM        !   Создает логическое имя
                         !
       SYS$CRELNT        !   Создает таблицу логических имен
                         !
       SYS$TRNLNM        !   Возвращает трансляцию логического
                         !
                         ! имени
                         !
       SYS$DELLNM        !   Уничтожает логическое имя
                         !
 
           Обычно логическое имя используется для передачи инфор-
 
      мации между двумя процессами. Если  оба  процесса  являются
 
      частью одного задания, то логическое имя можно поместить  в
 
      таблицу логических имен процесса (LNM$PROCESS) или в табли-
 
      цу логических имен задания (LNM$JOB).  Если  подпроцесс  не
 
      унаследует таблицу логических имен процесса, то связь между
 
      ними должна осуществляться  с  помощью  таблицы  логических
 
      имен задания. Если процессы  принадлежат  к  одной  группе,


                                - 144 -
 
                          00152-01 97 09-1
 
 
      нужно поместить логическое имя в  таблицу  логических  имен
 
      группы  LNM$GROUP  (это  требует  привилегии   GRPNAM   или
 
      SYSPRV). Если процессы не принадлежат к одной группе, нужно
 
      поместить логическое имя в таблицу логических имен  системы
 
      LNM$SYSTEM (это требует привилегии SYSNAM или SYSPRV). Мож-
 
      но также пользоваться идентификаторами, но только при сооб-
 
      щении между родительским и порожденным процессом, унаследо-
 
      вавшим идентификаторы родительского процесса.
 
           Например,  программа  CALC  порождает  подпроцесс  для
 
      выполнения   итерационных   вычислений.   Логическое    имя
 
      REF_NUMBER задает количество повторений работы для REPEAT -
 
      программы, работающей в подпроцессе. Поскольку и родительс-
 
      кий процесс, и подпроцесс являются частями одного и того же
 
      задания, REP_NUMBER помещается в  таблицу  логических  имен
 
      задания LNM$JOB:
 
      PROGRAM CALC
 
      !Переменная состояния и системные подпрограммы
 
      INTEGER*4 STATUS
 
      2         SYS$CRELNM,
 
      2         LIB$SET_EF,
 
      2         LIB$SPAWN
 
      !Определение структуры списка об'ектов
 
      STRUCTURE /ITMLST/
 
      UNION
 
      MAP
 
      INTEGER*2 BUFLEN
 
      INTEGER*2 CODE
 
      INTEGER*4 BUFADR
 
      INTEGER*4 RETLENADR
 
      END MAP


                                - 145 -
 
                          00152-01 97 09-1
 
 
      MAP
 
      INTEGER*4 END_LIST
 
      END MAP
 
      END UNION
 
      END STRUCTURE
 
      !Описание списка об'ектов
 
      CHARACTER*3 REPETITIONS_STR
 
      INTEGER REPETITIONS
 
      !Идентификаторы для LIB$SPAWN и SYS$CRELNM
 
      EXTERNAL CLI$M_NOLOGNAM,
 
      2        CLI$M_NOCLISYM,
 
      2        CLI$M_NOKEYPAD,
 
      2        CLI$M_NOWAIT,
 
      2        LNM$_STRING
 
      !Установить и создать логическое имя REP_NUMBER в
 
      !таблице задания
 
      LNMLIST(1).BUFLEN      = 3
 
      LNMLIST(1).CODE        = %LOC(LNM$_STRING)
 
      LNMLIST(1).BUFADR      = %LOC(REPETITIONS_STR)
 
      LNMLIST(1).RETLENADR   = 0
 
      LNMLIST(2).END_LIST    = 0
 
      STATUS = SYS$CRELNM (,
 
      2                    'LNM$JOB',    !Таблица лог. имен
 
      2                    'REP_NUMBER',,!Логическое имя
 
      2                    LNMLIST)      !Список, задающий
 
                                         !строку эквивалентности
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Выполнить REPEAT.FOR в подпроцессе
 
      MASK = %LOC (CLI$M_NOLOGNAM) .OR.
 
      2       %LOC (CLI$M_NOCLISYM) .OR.


                                - 146 -
 
                          00152-01 97 09-1
 
 
      2       %LOC (CLI$M_NOKEYPAD) .OR.
 
      2       %LOC (CLI$M_NOWAIT)
 
      STATUS = LIB$GET_EF (FLAG)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = LIB$SPAWN ('RUN REPEAT',,,MASK,,,,FLAG)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !
 
      !  Программа REPEAT.FOR
 
      !
 
      PROGRAM REPEAT
 
      !Повторяет вычисления REP_NUMBER раз, где REP_NUMBER
 
      !есть символическое имя
 
      !Переменные состояния и системные процедуры:
 
      INTEGER STATUS,
 
      2       SYS$TRNLNM,
 
      2       SYS$DELLNM
 
      !Число повторений
 
      INTEGER*4    REITERATE,
 
      2             REPEAT_STR_LEN
 
      CHARACTER*3   REPEAT_STR
 
      !Список об'ектов для SYS$TRNLNM
 
      !Определить структуру списка об'ектов
 
      STRUCTURE /ITMLST/
 
      UNION
 
      MAP
 
      INTEGER*2 BUFLEN
 
      INTEGER*2 CODE
 
      INTEGER*4 BUFADR
 
      INTEGER*4 RETLENADR
 
      END MAP


                                - 147 -
 
                          00152-01 97 09-1
 
 
      MAP
 
      INTEGER*4 END_LIST
 
      END MAP
 
      END UNION
 
      END STRUCTURE
 
      !Описать список об'ектов
 
      RECORD /ITMLST/ LNMLIST (2)
 
      !Определить код об'екта
 
      EXTERNAL LNM$_STRING
 
      !Установить и транслировать логическое имя REP_NUMBER
 
      LNMLIST(1).BUFLEN     = 3
 
      LNMLIST(1).CODE       = %LOC(LNM$_STRING)
 
      LNMLIST(1).BUFARD     = %LOC(REPEAT_STR)
 
      LNMLIST(1).RETLENADR  = %LOC(REPEAT_STR_LEN)
 
      LNMLIST(2).END_LIST   = 0
 
      STATUS = SYS$TRNLNM (,
 
      2                    'LNM$JOB',     !Таблица лог. имен
 
      2                    'REP_NUMBER',, !Логическое имя
 
      2                    LNMLIST)       !Список, требующий
 
                                          !строки эквивалентности
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Преобразование строки эквивалентности в целое
 
      !Пробелы игнорируются из-за BN
 
      READ (UNIT = REPEAT_STR (1:REPEAT_STR_LEN),
 
      2     FMT = '(BN,I3)') REITERATE
 
      !Вычисления
 
      DO 1 = 1, REITERATE
 
      !
 
      !Тело цикла
 
      !


                                - 148 -
 
                          00152-01 97 09-1
 
 
      END DO
 
      !Уничтожить логическое имя
 
      STATUS = SYS$DELLNM ('LNM$JOB',
 
      2                    'REP_NUMBER',)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
 
 
           3.4.2. Почтовые ящики
 
 
           Для создания почтового  ящика  используется  системная
 
      служебная процедура SYS$CREMBX. Эта процедура создает  поч-
 
      товый ящик и возвращает назначенный ему номер канала ввода-
 
      вывода. Обычно при вызове SYS$CREMBX задаются следующие два
 
      параметра:
 
           1) канал ввода-вывода (параметр 2). Указывается  пере-
 
      менная типа INTEGER*2 для получения  номера  канала  ввода-
 
      вывода. Этот параметр обязателен;
 
           2) логическое имя (параметр  7).  Задается  логическое
 
      имя, которое должно быть связано  с  почтовым  ящиком.  Это
 
      логическое имя идентифицирует почтовый ящик для других про-
 
      цессов и для операторов ввода-вывода языка фортран.
 
           Служебная системная процедура SYS$CREMBX также предос-
 
      тавляет возможность задавать размер сообщения, размер буфе-
 
      ра, код защиты почтового ящика и режим доступа к  почтовому
 
      ящику, но  обычно  можно  удовлетвориться  значениями  этих
 
      параметров по умолчанию.
 
           Например, создается почтовый ящик с  именем  MAIL_BOX.
 
      Номер канала ввода-вывода, присвоенный этому почтовому ящи-
 
      ку, засылается в MBX_CHAN:
 


                                - 149 -
 
                          00152-01 97 09-1
 
 
      !Канал ввода-вывода
 
      INTEGER*2 MBX_CHAN
 
      !Имя почтового ящика
 
      CHARACTER*(*) MBX_NAME
 
      PARAMETER (MBX_NAME = 'MAIL_BOX')
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,  !Канал в-в
 
      2                    ,,,,
 
      2                    MBX_NAME)  !Имя почт. ящика
 
 
 
           Примечание. Нельзя использовать MAIL в качестве  логи-
 
      ческого имени почтового ящика, иначе  система  в  ответ  на
 
      команду DCL MAIL не исполнит требуемый образ.
 
           По умолчанию почтовый ящик уничтожается, если нет свя-
 
      занного с ним  канала  ввода-вывода.  Такой  почтовый  ящик
 
      называется временным. Привелегия PRMMBX, позволяет  создать
 
      постоянный почтовый ящик (задав единицу в качестве парамет-
 
      ра PRMFLG при вызове SYS$CREMBX). Постоянный почтовый  ящик
 
      не уничтожается, пока не будет помечен на удаление  систем-
 
      ной служебной процедурой SYS$DELMBX (для этого нужна приве-
 
      легия PRMMBX). С того момента, как постоянный почтовый ящик
 
      помечен на удаление, он  становится  подобен  временному  и
 
      уничтожается, как только от него будет отсоединен последний
 
      канал ввода-вывода.
 
           Например, программный сегмент создает постоянный  поч-
 
      товый ящик, а затем порождает подпроцесс, который  помечает
 
      этот почтовый ящик на удаление:
 
 
      INTEGER STATUS,
 
      2       SYS$CREMBX
 


                                - 150 -
 
                          00152-01 97 09-1
 
 
      INTEGER*2 MBX_CHAN
 
      !Создать постоянный почтовый ящик
 
      STATUS = SYS$CREMBX (%VAL(1),     !Флаг 'постоянный
 
      2                    MBX_CHAN,    !канал'
 
      2                    ,,,,
 
      2                    'MAIL_BOX')  !Логическое имя
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Создать подпроцесс для его уничтожения
 
      STATUS = LIB$SPAWN ('RUN DELETE_MBX')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
 
           Подпроцесс назначает почтовому  ящику  канал  и  затем
 
      использует  этот  канал  для  уничтожения  почтового  ящика
 
      (любой процесс, удаляющий постоянный, не созданный им самим
 
      почтовый ящик, должен пользоваться этим способом. При  наз-
 
      начении  канала  почтовому  ящику  используется   процедура
 
      SYS$ASSIGN):
 
      INTEGER STATUS,
 
      2       SYS$DELMBX,
 
      2       SYS$ASSIGN
 
      INTEGER*2 MBX_CHAN
 
      !Назначить почтовому ящику канал
 
      STATUS = SYS$ASSIGN ('MAIL_BOX',
 
      2                    MBX_CHAN,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
 
      !Удалить почтовый ящик
 
      STATUS = SYS$DELMBX (%VAL(MBX_CHAN))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 


                                - 151 -
 
                          00152-01 97 09-1
 
 
           Использование почтового ящика (запись в него и  чтение
 
      из него) возможно в трех режимах:
 
           1) синхронный. Производится запись (или чтение) в поч-
 
      товый ящик (или из него), а потом следует ожидание обратной
 
      операции. Применяются операторы ввода-вывода языка фортран.
 
      Этот способ работы с почтовым ящиком рекомендуется;
 
           2) непосредственный. Производится  запись  в  почтовый
 
      ящик или чтение из него. Выполнение программы  продолжается
 
      после завершения этой операции. Применяется системная  слу-
 
      жебная процедура SYS$QIOW;
 
           3) асинхронный. Запрос на запись в почтовый  ящик  или
 
      чтение из него ставится  в  очередь.  Выполнение  программы
 
      продолжается во время выполнения запроса. Используется сис-
 
      темная слежебная процедура SYS$QIO. По окончании чтения или
 
      записи блок состояния возврата (если он задан) заполняется,
 
      флаг события (если был задан) устанавливается и выполняется
 
      подпрограмма AST (если была задана).
 
 
 
           3.4.2.1. Синхронный ввод-вывод
 
 
           Синхронный ввод-вывод применяется для чтения или запи-
 
      си информации от другого образа или в него, если  продолже-
 
      ние работы невозможно до ответа корреспондента.
 
           Для открытия почтового  ящика  операциями  фортранного
 
      ввода-вывода используется оператор OPEN с такими специфика-
 
      торами: UNIT, FILE,  CARRIAGECONTROL  и  STATUS.  Значением
 
      ключевого слова FILE должно быть логическое  имя  почтового
 
      ящика (SYS$CREMBX дает возможность связать почтовый яящик с
 
      логическим именем, когда почтовый ящик создается). Значение
 
      ключевого слова CARRIAGECONTROL должно быть 'LIST'.  Значе-
 


                                - 152 -
 
                          00152-01 97 09-1
 
 
      ние лючевого слова STATUS должно  быть  'NEW'  для  первого
 
      оператора OPEN и 'OLD' для всех последующих.
 
 
           Пример.
 
      ! Выполнение первого открытия почтового ящика
 
      ! Переменная состояния и значения
 
      INTEGER STATUS
 
      ! Логическое устройство и имя для почтового ящика
 
      INTEGER MBX_LUN
 
      CHARACTER(*) MBX_NAME
 
      PARAMETER (MBX_NAME = MAIL_BOX)
 
      ! Создать почтовый ящик
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,     !Канал
 
      2                    ,,,,
 
      2                    MBX_NAME)      !Логическое имя
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Принять логическое имя для почтового ящика и открыть
 
      !почтовый ящик
 
      STATUS = LIB$GET_LUN (MBX_LUN)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      OPEN (UNIT = MBX_LUN,
 
      2     FILE = MBX_NAME,
 
      2     CARRIAGECONTROL = 'LIST',
 
      2     STATUS = 'NEW')
 
 
           Когда почтовый ящик открыт, к нему можно обращаться  с
 
      помощью форматных операторов последовательного доступа READ
 
      и WRITE языка фортран. СП ФОРТРАН автоматически синхронизи-
 
      рует ввод-вывод, не позволяя образу закончить операцию вво-
 
      да-вывода до тех пор, пока образ-корреспондент не  выполнит


                                - 153 -
 
                          00152-01 97 09-1
 
 
      противоположную операцию  (если  образ  выполняет  операцию
 
      чтения из почтового ящика, управление этому образу не будет
 
      возвращено до тех пор, пока сотрудничающий с ним  образ  не
 
      выполнит операцию записи в почтовый ящик).
 
 
           Пример.
 
           Образ-корреспондент передает адресату имена устройств.
 
      Образ-адресат возвращает имя процесса и терминал, связанный
 
      с процессом, за  которым  устройство  закреплено.  Операции
 
      записи (WRITE) из первого образа не  заканчиваются  до  тех
 
      пор, пока сотрудничающий процесс не выполнит операцию READ:
 
      ! Программа-корреспондент
 
      PROGRAM PROCESS_DEVICE
 
      ! Переменная состояния
 
      INTEGER STATUS
 
      ! Имя и канал в/в для почтового ящика
 
      CHARACTER*(*) MBX_NAME
 
      PARAMETER (MBX_NAME = 'MAIL_BOX')
 
      INTEGER*2 MBX_CHAN
 
      ! Номер логического устройства для фортранного ввода-вывода
 
      INTEGER MBX_LUN
 
      ! Формат строки символов
 
      CHARACTER*(*) CHAR_FMT
 
      PARAMETER (CHAR_FMT = '(A50)')
 
      ! Сообщение для почтового ящика
 
      CHARACTER*50 MBX_MESSAGE
 
      ! Создать почтовый ящик
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,  ! Канал
 
      2                    ,,,,
 


                                - 154 -
 
                          00152-01 97 09-1
 
 
      2                    MBX_NAME)  ! Логическое имя
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Принять логическое имя для почтового ящика и открыть
 
      ! почтовый ящик
 
      STATUS = LIB$GET_LUN (MBX_LUN)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      OPEN (UNIT = MBX_LUN,
 
      2     FILE = MBX_NAME,
 
      2     CARRIAGECONTROL = 'LIST',
 
      2     STATUS = 'NEW')
 
      ! Создать подпроцесс для выполнение GETDEVINE.EXE
 
      STATUS = SYS$CREPRC (,
 
      2                    'GETDEVINE',   ! Образ
 
      2                    ,,,,,
 
      2                    GET_DEVICE',   ! Имя подпроцесса
 
      2                    %VAL(4),,,)    ! Приоритет
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Передать имена устройств в GETDEVINE
 
      WRITE (UNIT=MBX_LUN,
 
      2      FMT=CHAR_FMT) 'SYS$DRIVEO'
 
      ! Читать информацию об устройствах из GETDEVINE
 
      READ (UNIT=MBX_LUN,
 
      2     FMT=CHAR_FMT) MBX_MESSAGE
 
      END
 
 
 
      ! Программа - адресат
 
      ! СозданиE MAILBOX
 
      STATUS = SYS$CREMBX (,
 
      2                     MBX_CHAN,   !Канал ввода-вывода
 
      2                     ,,,,


                                - 155 -
 
                          00152-01 97 09-1
 
 
      2                     MBX_NAME)   !Имя почтового ящика
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      ! Получение логического номера и открытие почт. ящика
 
      STATUS = LIB$GET_LUN (MBX_LUN)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      OPEN (UNIT=MBX_LUN,
 
      2     FILE=MBX_NAME,
 
      2     CARRIAGECONTROL='LIST',
 
      2     STATUS = 'OLD')
 
      ! Читать имена устройств из почтового ящика
 
      READ (UNIT=MBX_LUN,
 
      2     FMT=CHAR_FMT) MBX_MESSAGE
 
      !Для нахождения процесса и терминала используется
 
      !SYS$GETJPI
 
      !Имя процесса
 
      !Имя терминала
 
      MBX_MESSAGE = MBX_MESSAGE//' '//
 
      2              PROG_NAME(1:P_LEN)//' '//
 
      2              TERM(1:T_LEN)
 
      !Информацию об устройстве записать в DEVICE
 
      WRITE (UNIT=MBX_LUN,
 
      2      FMT=CHAR_FMT) MBX_MESSAGE
 
      END
 
 
 
           3.4.2.2. Непосредственный ввод-вывод
 
 
           Непосредственный ввод-вывод используетсяя  для  чтения
 
      из другого образа или записи в него  без  ожидания  ответа.
 
      Гарантией получения другим образом посланной ему информации
 
      является:
 


                                - 156 -
 
                          00152-01 97 09-1
 
 
           1) ожидание назначения канала вторым  процессом  этому
 
      почтовому ящику;
 
           2) использование постоянного почтового ящика.
 
           Для постановки запроса в очередь  на  непосредственный
 
      ввод-вывод  используется  системная   служебная   процедура
 
      SYS$QIOW с параметрами (см. [4]):
 
           1) канал ввода-вывода (параметр 2). Необходимо исполь-
 
      зовать  канал,  возвращенный  процедурой  SYS$ASSIGN.  Этот
 
      параметр  должен  быть  словом,  передаваемым  по  значению
 
      (%VAL);
 
           2) код функции и модификаторы (параметр 3). В качестве
 
      функции можно задать одну из следующих (системная служебная
 
      процедура  SYS$QIOW  без  модификатора  IO$M_NOW  выполняет
 
      ввод-вывод синхронно):
 
      IO$_WRITEVBLK.OR.IO$M_NOW - писать сообщение;
 
      IO$_READVBLK.OR.IO$M_NOW  - читать сообщение;
 
      IO$_WRITEOF.OR.IOSM_NOW   - писать сообщение "конец файла".
 
           Эти идентификаторы определены в модуле $IODEF  библио-
 
      теки системных об'ектных или разделяемых образов и  в  биб-
 
      лиотеке определений СП ФОРТРАН (параметр должен передавать-
 
      ся по значению %VAL);
 
           3) блок состояния (параметр 4). Определить  блок  сос-
 
      тояния, состоящий из кода состояния возврата (слово), вели-
 
      чины сообщения (слово) и идентификатора  процесса  (двойное
 
      слово PID) посылающего  (для  чтения)  или  воспринимающего
 
      (для записи). Чтение данных из почтового ящика нужно выпол-
 
      нять, ссылаясь на них, как на  подстроку,  используя  длину
 
      сообщения, возвращенную в блоке состояния. Ссылка на  буфер
 
      целиком приведет к рассмотрению лишних символов, оставшихся
 
      в нем от предыдущих операций;


                                - 157 -
 
                          00152-01 97 09-1
 
 
           4) буфер пользователя (параметр 7). Буфер пользователя
 
      нужно определить как символьную переменную, но ссылаться на
 
      нее при обращении к SYS$QIOW необходимо по  ссылке  (%REF).
 
      При записи сообщения "конец файла" процедурой SYS$QIOW этот
 
      параметр не учитывается;
 
           5) размер буфера пользователя (параметр  8,  или  P2).
 
      Задается число символов, отведенных под буфер пользователя.
 
      Например, если USER_BUFFER определен как CHARACTER*132,  то
 
      параметр USER_BUFFER_SIZE со значением 132  должен  переда-
 
      ваться по значению. При записи сообщения "конец файла" этот
 
      параметр игнорируется.
 
           Так как непосредственный ввод-вывод является асинхрон-
 
      ным, то в почтовом ящике в момент чтения  может  находиться
 
      более одного (или ни одного)  сообщения.  Если  в  почтовом
 
      ящике более одного сообщения, то операция чтения подает  их
 
      по одному в том порядке, в котором  они  записаны.  Если  в
 
      почтовом ящике нет ни одного сообщения, то операция  чтения
 
      порождает ошибку "конец файла".
 
           Для того, чтобы дать возможность принимающей программе
 
      различать пустой почтовый ящик и конец предназначенных  для
 
      передачи данных, передающий процесс должен использовать код
 
      функции IO$_WRITEOF для записи в  почтовый  ящик  сообщения
 
      "конец файла" в качестве последней порции передаваемых дан-
 
      ных. Когда принимающая программа читает из пустого почтово-
 
      го ящика, возвращается сообщение о  конце  файла  и  второе
 
      длинное слово в блоке состояния ввода-вывода нулевое. Когда
 
      же программа читает сообщение о конце файла, явно  записан-
 
      ное в почтовый ящик, то возвращается сообщение о конце фай-
 
      ла и второе длинное слово в блоке состояния содержит  иден-
 
      тификационный номер процесса, записавшего сообщение.


                                - 158 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Программа SEND.FOR  создает  почтовый  ящик  с  именем
 
      MAIL_BOX, посылает туда данные, после  них  -  сообщение  о
 
      конце файла; программа RECEIVE.FOR создает почтовый ящик  с
 
      тем же логическим именем, получает сообщения  из  почтового
 
      ящика в некоторый массив, заканчивая  операцию  чтения  при
 
      обнаружении записи о конце файла с нулевым  вторым  длинным
 
      словом в блоке состояния  ввода-вывода  (это  подтверждает,
 
      что сообщение о конце файла было послано записывающим  про-
 
      цессом). В процессах используется общий флаг событий 64 для
 
      гарантии  того,  что  SEND.FOR  не  закончит  работу,  пока
 
      RECEIVE.FOR не установит  канал  к  почтовому  ящику  (если
 
      RECEIVE.FOR будет выполняться первой, произойдет  ошибка  в
 
      связи с тем, что SYS$ASSIGN не найдет почтового ящика):
 
      !SEND.FOR
 
      INTEGER*4 STATUS
 
      !Имя и номер канала для почтового ящика
 
      CHARACTER*(*) MBX_NAME
 
      PARAMETER (MBX_NAME = 'MAIL_BOX')
 
      INTEGER*2 MBX_CHAN
 
      !Сообщение в почтовый ящик
 
      CHARACTER*80 MBX_MESSAGE
 
      INTEGER LEN
 
      CHARACTER*80 MESSAGE (255)
 
      INTEGER MESSAGE_LEN (255)
 
      INTEGER MAX_MESSAGE
 
      PARAMETER (MAX_MESSAGE = 255)
 
      !Коды функций ввода-вывода и блок состояния
 
      INCLUDE '($IODEF)'
 
      INTEGER*4 WRITE_CODE


                                - 159 -
 
                          00152-01 97 09-1
 
 
      STRUCTURE /STATUS_BLOCK/
 
      INTEGER*2 IOSTAT,
 
      2         MSG_LEN
 
      INTEGER*4 READER_PID
 
      END STRUCTURE
 
      RECORD /STATUS_BLOCK/ IOSTATUS
 
      !Системные программы
 
      INTEGER SYS$CREMBX,
 
      2       SYS$ASCEFC,
 
      2       SYS$WAITFR,
 
      2       SYS$QIOW
 
      !Создать почтовый ящик
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,
 
      2                    ,,,,
 
      2                    MBX_NAME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Заполнить массив MESSAGE
 
      !Писать сообщения
 
      DO I = 1, MAX_MESSAGE
 
      WRITE_CODE = IO$_WRITEVBLK .OR. IO$M_NOW
 
      MBX_MESSAGE = MESSAGE(I)
 
      LEN = MESSAGE_LEN(I)
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL (MBX_CHAN),    !Канал
 
      2                  %VAL (WRITE_CODE),  !Код ввода-вывода
 
      2                  IOSTATUS,           !Блок состояния
 
      2                  ,,
 
      2                  %REF(MBX_MESSAGE),  !P1
 
      2                  %VAL(LEN),,,,)      !P2


                                - 160 -
 
                          00152-01 97 09-1
 
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF (.NOT. IOSTATUS.IOSTAT)
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS.STATUS))
 
      END DO
 
      !Писать конец файла
 
      WRITE_CODE = IO_WRITEOF .OR. IO$M_NOW
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL(MBX_CHAN),   ! Канал
 
      2                  %VAL(WRITE_CODE), ! Код конца файла
 
      2                  IOSTATUS,         ! Блок завершения
 
      2                  ,,,,,,, )
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF (.NOT. IOSTATUS.IOSTAT)
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS.IOSTAT))
 
      !Убедиться, что сотрудничающий процесс может прочитать
 
      !информацию, дождавшись, пока он установит канал на
 
      !почтовый ящик
 
      STATUS = SYS$ASCEFC (%VAL(64),
 
      2                     'CLUSTER',,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$WAITFR (%VAL(64))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
      !RECEIVE.FOR
 
      INTEGER STATUS
 
      INCLUDE '($IODEF)'
 
      INCLUDE '($SSDEF)'
 
      !Имя и номер канала для почтового ящика
 
      CHARACTER*(*) MBX_NAME
 
      PARAMETER (MBX_NAME = 'MAIL_BOX')


                                - 161 -
 
                          00152-01 97 09-1
 
 
      INTEGER*2 MBX_CHAN
 
      !Код функции QIO
 
      INTEGER READ_CODE
 
      !Сообщение из почтового ящика
 
      CHARACTER*80 MBX_MESSAGE
 
      INTEGER*4    LEN
 
      !Массивы сообщений
 
      CHARACTER*80 MESSAGE (255)
 
      INTEGER*4     MESSAGE_LEN (255)
 
      !Блок состояния ввода-вывода
 
      STRUCTURE /STATUS_BLOCK/
 
      INTEGER*2 IOSTAT,
 
      2         MSG_LEN
 
      INTEGER*4 READER_PID
 
      END STRUCTURE
 
      RECORD /STATUS_BLOCK/ IOSTATUS
 
      !Системные подпрограммы
 
      INTEGER SYS$ASSING,
 
      2       SYS$ASCEFC,
 
      2       SYS$SETEF,
 
      2       SYS$QIOW
 
      !Создать почтовый ящик и дать знать об этом другим
 
      !процессам
 
      STATUS = SYS$ASSIGN (MBX_NAME,
 
      2                   MBX_CHAN,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$ASCEFC (%VAL(64),
 
      2                     'CLUSTER',,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$SETEF (%VAL(64))


                                - 162 -
 
                          00152-01 97 09-1
 
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Читать первое сообщение
 
      READ_CODE = IO$_READVBLK .OR. IO$M_NOW
 
      LEN = 80
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL(MBX_CHAN),   !Канал
 
      2                  %VAL(READ_CODE),   !Код функций
 
      2                  IOSTATUS,          !Блок состояния
 
      2                  ,,
 
      2                  %REF(MBX_MESSAGE), !P1
 
      2                  %VAL(LEN),,,,)     !P2
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF ((.NOT. IOSTATUS.IOSTAT) .AND.
 
      2  (IOSTATUS.IOSTAT .NE. SS$ENDOFFILE)) THEN
 
      CALL LIB$SIGNAL (%VAL(IOSTATUS.IOSTAT))
 
      ELSE IF (IOSTATUS.IOSTAT .NE. SS$_ENDOFFILE) THEN
 
      I = 1
 
      MESSAGES(I) = MBX_MESSAGE
 
      MESSAGE_LEN(I) = IOSTATUS.MSG_LEN
 
      END IF
 
      !Читать сообщения до тех пор, пока пишущий процесс не
 
      !запишет конец файла
 
      DO WHILE (.NOT. ((IOSTATUS.IOSTAT .EQ. SS$_ENDOFFILE) .AND.
 
      2                (IOSTATUS.READER_PID .NE. 0)))
 
      STATUS = SYS$QIOW (,
 
      2                  %VAL(MBX_CHAN),   !Канал
 
      2                  %VAL(READ_CODE),  !Код функции
 
      2                  IOSTATUS,         !Блок состояния
 
      2                  ,,
 
      2                  %REF(MBX_MESSAGE),!P1


                                - 163 -
 
                          00152-01 97 09-1
 
 
      2                  %VAL(LEN),,,)     !P2
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      IF ((.NOT. IOSTATUS.IOSTAT) .AND.
 
       2   (IOSTATUS.IOSTAT .NE. SS$ENDOFFILE)) THEN
 
      I = I + 1
 
      MESSAGES(I) = MBX_MESSAGE
 
      MESSAGE_LEN(I) = IOSTATUS.MSG_LEN
 
      END IF
 
      END DO
 
      !                      .
 
      ! Продолжение прграммы .
 
      !                      .
 
 
 
           3.4.2.3. Асинхронный ввод-вывод
 
 
           Асинхронный ввод-вывод используется для  постановки  в
 
      очередь запроса на чтение из почтового ящика или  записи  в
 
      него. Для уверенности, что другой процесс получит  записан-
 
      ную информацию, необходимо:
 
           1) не заканчивать работу, пока другой процесс не уста-
 
      новит канал к тому же почтовому ящику;
 
           2) применять постоянный почтовый ящик.
 
           Для постановки в очередь асинхронного запроса на ввод/
 
      вывод используется системная  служебная  процедура  SYS$QIO
 
      без модификатора IO$M_NOW.  Системная  служебная  процедура
 
      SYS$QIO позволяет задать AST,  которое  будет  выполнено  и
 
      флаг события, который будет установлен в момент  завершения
 
      операции ввода-вывода [4], [7].
 
 
           Пример.
 
           Подсчет валового дохода,  налогов,  получение  чистого


                                - 164 -
 
                          00152-01 97 09-1
 
 
      дохода в программах INCOME.FOR, CALC_TAXES и INSTALLED.FOR.
 
      Программа INCOME использует SYS$CREPRC для создания  почто-
 
      вого ящика, записи в него сообщения о прекращении, создания
 
      подпроцесса подсчета налогов (CALC_TAXES); INCOME подсчиты-
 
      вает валовой доход. INCOME выставляет асинхронный запрос на
 
      чтение к почтовому ящику,  задавая  флаг  события,  который
 
      должен быть установлен по завершению чтения (чтение  завер-
 
      шается, когда CALC_TAXES завершает созданный процесс,  зас-
 
      тавляя систему писать в почтовый ящик сообщение о прекраще-
 
      нии). После окончания подсчета валового  дохода  INCOME.FOR
 
      ждет флага, показывающего, что CALC_TAXES завершила работу,
 
      а затем вычисляет  чистый  доход.  CALC_TAXES.FOR  передает
 
      налоговую информацию в INCOME.FOR, используя  установленный
 
      блок общей памяти, созданный из программы INSTALLED.FOR:
 
      !INSTALLED.FOR
 
      !Установить блок общей памяти, который будет связан
 
      !с INCOME.FOR и CALC_TAXES.FOR
 
      !Если разделяемый образ, созданный из этого файла, не
 
      !есть из INSTALLED и приравнять его к полной
 
      !спецификации файла разделяемого образа
 
      INTEGER*4 INCOME (200),
 
      2         TAXES (200),
 
      2         NET (200)
 
      COMMON /CALC/ INCOME,
 
      2             TAXES,
 
      2             NET
 
      END
 
      !INCOME.FOR
 
      !Состояние и системные процедуры
 
      INCLUDE '($SSDEF)'


                                - 165 -
 
                          00152-01 97 09-1
 
 
      INCLUDE '($IODEF)'
 
      INTEGER STATUS,
 
      2       LIB$GET_LUN,
 
      2       LIB$GET_EF,
 
      2       SYS$CLREF,
 
      2       SYS$CREMBX,
 
      2       SYS$CREPRC,
 
      2       SYS$GETDVIW,
 
      2       SYS$QIO,
 
      2       SYS$WAITFR
 
      !Установить для SYS$GETDVI
 
      !Определить структуру списка об'ектов
 
      STRUCTURE /ITMLST/
 
      UNION
 
      MAP
 
      INTEGER*2 BUFLEN
 
      INTEGER*2 CODE
 
      INTEGER*4 BUFADR
 
      INTEGER*4 RETLENADR
 
      END MAP
 
      MAP           
 
      INTEGER*4 END_LIST
 
      END MAP
 
      END UNION
 
      END STRUCTURE
 
      !Описать структуру списка об'ектов
 
      RECORD /ITMLST/ DVILIST (2)
 
      INTEGER*4 UNIT_BUF,
 
      2         UNIT_LEN
 
      EXTERNAL DVI$_UNIT


                                - 166 -
 
                          00152-01 97 09-1
 
 
      !Имя и канал ввода-вывода для почтового ящика
 
      CHARACTER*(*) MBX_NAME
 
      PARAMETER (MBX_NAME = 'MAIL_BOX')
 
      INTEGER*2 MBX_CHAN
 
      INTEGER*4 MBX_LUN         !Номер логического устройства
 
                                !для ввода-вывода
 
      CHARACTER*84 MBX_MESSAGE  !Сообщения почтового ящика
 
      INTEGER*4 READ_CODE,
 
      2         LENGTH
 
      !Блок состояния ввода-вывода
 
      STRUCTURE /STATUS_BLOCK/
 
      INTEGER*2 IOSTAT,
 
      2         MSG_LEN
 
      INTEGER*4 READER_PID
 
      END STRUCTURE
 
      RECORD /STATUS_BLOCK/ IOSTATUS
 
      !Описание переменных вычисления в установленном блоке
 
      !общей памяти
 
      INTEGER*4 INCOME (200),
 
      2         TAXES (200),
 
      2         NET (200)
 
      COMMON /CALC/ INCOME,
 
      2             TAXES,
 
      2             NET
 
      !Флаг, указывающий, что налоги подсчитаны
 
      INTEGER*4 TAX_DONE
 
      !Получить и сбросить флаг события
 
      STATUS = LIB$GET_EF (TAX_DONE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$CLREF (%VAL(TAX_DONE)


                                - 167 -
 
                          00152-01 97 09-1
 
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Получить и сбросить флаг события
 
      STATUS = LIB$GET_EF (TAX_DONE)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      STATUS = SYS$CLREF (%VAL(TAX_DONE))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      !Создать почтовый ящик
 
      STATUS = SYS$CREMBX (,
 
      2                    MBX_CHAN,
 
      2                    ,,,,
 
      2                    MBX_NAME)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      !Принять номер устройства почтового ящика
 
      DVILIST(1).BUFLEN     = 4
 
      DVILIST(1).CODE       = %LOC(DVI$_UNIT)
 
      DVILIST(1).BUFARD     = %LOC(UNIT_BUF)
 
      DVILIST(1).RETLENADR  = %LOC(UNIT_LEN)
 
      DVILIST(2).END_LIST   = 0
 
      STATUS = SYS$GETDVIW (,
 
      2                     %VAL(MBX_CHAN),  !Канал
 
      2                     MBX_NAME,        !Устройсво
 
      2                     DVILIST,         !Список об'ектов
 
      2                     ...)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      ! Создание подпроцесса для вычисления дохода
 
      STATUS = SYS$CREPRC (,
 
      2                   'CALC_TAXES',  !Образ
 
      2                   ,,,,,
 
      2                   'CALC_TAXES',  !Имя процесса
 
      2                   %VAL(4),       !Приоритет


                                - 168 -
 
                          00152-01 97 09-1
 
 
      2                   ,
 
      2                   %VAL(UNIT_BUF),)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      !Асинхронное чтение из почтового ящика для сообщения о
 
      !прекращении устанавливает флаг по окончании подсчета
 
      !налогов
 
      READ_CODE = IO$_READVBLK
 
      LENGTH = 84
 
      STATUS = SYS$QIO (%VAL(TAX_DONE),    !Индицирует
 
                                           !завершение чтения
 
      2                %VAL(MBX_CHAN),     !CHANNEL
 
      2                %VAL(READ_CODE),    !Код функции
 
      2                IOSTATUS,,,         !Блок состояния
 
      2                %REF(MBX_MESSAGE),  !P1
 
      2                %VAL(LENGTH),,,,)   !P2
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Подсчет доходов
 
      !Ждать окончания подсчета налогов
 
      STATUS = SYS$WAITFR (%VAL(TAX_DONE))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Проверить ввод-вывод почтового ящика
 
      IF (.NOT. IOSTATUS.IOSTAT)
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS.IOSTAT))
 
      !Подсчитать чистый доход после уплаты налогов
 
      END
 
       !Ждать окончания подсчета налогов
 
      STATUS = SYS$WAITFR (%VAL(TAX_DONE))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Проверить ввод-вывод почтового ящика
 
      IF (.NOT. IOSTATUS.IOSTAT)


                                - 169 -
 
                          00152-01 97 09-1
 
 
      2    CALL LIB$SIGNAL (%VAL(IOSTATUS.IOSTAT))
 
      !Подсчитать чистый доход после уплаты налогов
 
      END
 
      !CALC_TAXES.FOR
 
      !Описание переменных вычисления в установленном блоке
 
      !общей памяти
 
      INTEGER*4 INCOME (200),
 
      2         TAXEN (200),
 
      2         NET (200)
 
      COMMON /CALC/ INCOME,
 
      2             TAXES,
 
      2             NET
 
      !Подсчет налогов
 
      END
 
 
 
           3.4.3. Разделяемые данные
 
 
           Обычно для сообщений между процессами или для одновре-
 
      менного доступа различных процессов к одним и тем же данным
 
      применется установленный блок общей памяти.  Для  установки
 
      блока общей памяти требуется привелегия CMKRNL.  Если  этой
 
      привилегии нет, то те же операции можно выполнить с помощью
 
      глобальной секции.
 
 
 
           3.4.3.1. Установленные блоки общей памяти
 
 
           Для обмена информацией между процессами с помощью бло-
 
      ка общей памяти необходимо установить блок общей памяти как
 
      разделяемый образ и скомпоновать с  ним  каждую  программу,
 
      имеющую на него ссылку.
 
           Для установки блока общей памяти в качестве  разделяе-


                                - 170 -
 
                          00152-01 97 09-1
 
 
      мого образа нужно:
 
           1) создать блок общей памяти.  Написать  программу  на
 
      языке фортран, описывающую переменные в блоке общей  памяти
 
      и определяющую этот блок.  Программа  не  должна  содержать
 
      выполняемых операторов, например:
 
      ! INC_COMMON.FOR содержит описание COMMON-блока
 
      INTEGER TOTAL_HOUSES
 
      REAL PERSONS_HOUSE (2048),
 
      2    ADULTS_HOUSE (2048),
 
      2    INCOME_HOUSE (2048)
 
      COMMON /INCOME_DATA/ TOTAL_HOUSES,
 
      2                    PERSONS_HOUSE,
 
      2                    ADULTS_HOUSE,
 
      2                    INCOME_HOUSE
 
      END
 
           2) создать разделяемый образ. Оттранслировать програм-
 
      му, содержащую блок общей памяти. Для создания разделяемого
 
      образа,  содержащего  блок  общей  памяти,  воспользоваться
 
      командой LINK/SHAREABLE:
 
      $ FORTRAN INC_COMMON
 
      $ LINK/SHAREABLE INC_COMMON
 
           3) установить  разделяемый  образ,  используя  команду
 
      языка DCL SET PROCESS/PRIVILEGE для  разрешения  привилегии
 
      CMKRNL, требуемой для обслуживающей программы установления.
 
      Вызов интерактивной  обслуживающей  программы  установления
 
      выполняется  командой  INSTALL.  В   ответ   на   подсказку
 
      INSTALL>, вводится CREATE, затем полная спецификация  файла
 
      разделяемого образа, содержащего  блок  общей  памяти  (тип
 
      файла  по  умолчанию  считается   EXE),   и   квалификаторы
 
      /WRITEABLE и /SHARED. Обслуживающая программа устанавливает


                                - 171 -
 
                          00152-01 97 09-1
 
 
      указанный разделяемый образ и повторяет подсказку INSTALL>.
 
      Для завершения нужно ввести EXIT, затем отменить привилегию
 
      CMKRNL:
 
      $ SET PROCESS/PRIVILEGE=CMKRNL
 
      $ INSTALL
 
      INSTALL> CREATE DISK$USER:[INCOME.DEV]INC_COMMON-
 
      _>/WRITEABLE/SHARED
 
      INSTALL> EXIT
 
      $ SET PROCESS/PRIVILEGE=NOCMKRLN
 
           Примечание. Диск, содержащий установленный  образ,  не
 
      может  быть  размонтирован.  Снятие  установленного  образа
 
      выполняется обслуживающей программой  установления,  вводом
 
      после полной спецификации файла образа команды DELETE.  Эта
 
      подкоманда не уничтожает файл на диске; она убирает файл из
 
      списка известных установленных образов.
 
           Для чтения  или  записи  данных  установленного  блока
 
      общей памяти из любой программы необходимо сделать  следую-
 
      щее:
 
           1) включить в  программу  определение  того  же  блока
 
      общей памяти и переменных;
 
           2) оттранслировать программу;
 
           3)  скомпоновать  программу  с  разделяемым   образом,
 
      содержащим блок  общей  памяти  (компоновка  с  разделяемым
 
      образом требует файла  возможностей).  Например,  программы
 
      INCOME и REPORT имеют доступ к одной области памяти в блоке
 
      INCOME_DATA (файл INC_COMMON.FOR):
 
      $ LINK INCOME, DATA/OPTION
 
      $ LINK REPORT, DATA/OPTION
 
      Причем файл DATA.OPT содержит:
 
      INC_COMMON/SHAREABLE


                                - 172 -
 
                          00152-01 97 09-1
 
 
           4) выполнить программу.
 
           Обычно программы, имеющие доступ к разделяемым данным,
 
      для синхронизации доступа к этим  данным  на  чтение  и  на
 
      запись используют кластер флагов общих событий. В  простей-
 
      шем случае один флаг события из кластера общих флагов собы-
 
      тий может означать, что программа читает данные. Перед дос-
 
      тупом к разделяемым данным программа  должна  проанализиро-
 
      вать кластер общих флагов  событий.  Необходимо  убедиться,
 
      что доступ к данным не вступит в  конфликт  с  происходящей
 
      уже операцией.
 
 
 
           3.4.3.2. Глобальные секции
 
 
           Для совместного использования данных в глобальных сек-
 
      циях каждый процесс,  планирующий  доступ  к  этим  данным,
 
      включает в себя блок общей памяти с одним и тем же  именем,
 
      содержащий переменные под эти данные. Первый  из  процессов
 
      для ссылки на данные об'являет блок общей памяти глобальной
 
      секцией и, возможно, отображает данные в эту секцию (данные
 
      в глобальных секциях, как и в личных, должны быть выравнены
 
      по краю страницы).
 
           Для создания глобальной секции вызывается SYS$CPMPSC с
 
      обязательным указанием следующих параметров:
 
           - добавочный параметр, имя  глобальной  секции  (пара-
 
      метр 5). Программы используют это имя для  доступа  к  гло-
 
      бальной секции;
 
           - дополнительный  флаг   в   параметре   флагов,   бит
 
      SEC$V_GBL для об'явления секции глобальной.
 
           Все остальные программы, желающие  получить  доступ  к
 
      данным глобальной секции, могут использовать SYS$CPMPSC или
 


                                - 173 -
 
                          00152-01 97 09-1
 
 
      SYS$MGBLSC для занесения данных в глобальную  секцию.  Если
 
      известно, что глобальная секция уже существует, лучше  при-
 
      менять системную служебную процедуру SYS$MGBLSC.
 
           Системной служебной процедуре SYS$MGBLSC [4]  задаются
 
      обычно следующие параметры (остальные - по умолчанию):
 
           - секция для отображения (параметр 1). Задается  пара-
 
      метр как целый массив из двух элементов.  Положение  первой
 
      переменной в блоке общей памяти задается значением  первого
 
      элемента  массива,  а  значение  второго  элемента  массива
 
      задает положение последней переменной в блоке (если  первая
 
      переменная в блоке есть строка или массив,  то  указывается
 
      первый элемент строки или массива; если же последняя  пере-
 
      менная является строкой или массивом, указывается последний
 
      элемент строки или массива).
 
           Программные переменные под данные  находятся  в  блоке
 
      общей памяти, который должен быть выравнен по границе стра-
 
      ницы во время компоновки командой компоновщика ("имя"  есть
 
      имя блока общей памяти):
 
      PSECT_ATTR = имя, PAGE
 
      внутри блока общей памяти данные следует задавать в порядке
 
      от наиболее сложных к наименее сложным (в порядке понижения
 
      ранга), символьные данные должны быть в конце. Это приводит
 
      к естественному выравниванию  данных,  предотвращая  нечет-
 
      ность границы страниц в виртуальной памяти;
 
           - секция отображения (параметр 2).  Задается  параметр
 
      как двухэлементный целый массив. Значение,  возвращенное  в
 
      первом элементе массива, должно быть то же самое, что  было
 
      передано в качестве  адреса  в  первом  элементе  параметра
 
      INADR. Значение, возвращенное во  втором  элементе,  должно
 
      быть равно или превосходить (в пределах 512 байт,  1  блок)


                                - 174 -
 
                          00152-01 97 09-1
 
 
      значение, переданное во втором элементе параметра INADR;
 
           - возможности (параметр 4). Задается SEC$V_GBL,  приз-
 
      нак глобальной секции;
 
           - имя секции (параметр 5). Задается  строка  символов,
 
      содержащая имя отображаемой глобальной секции.
 
 
           Пример.
 
           Образ DEVICE.FOR передает образу  CETDEVINE.FOR  имена
 
      усройств. CETDEVINE.FOR возвращает имя процесса, за которым
 
      закреплено устройство, и терминал этого процесса - для каж-
 
      дого устройства. Эти  два  процесса  используют  глобальную
 
      секцию CLOBAL_SEC; секция отображена в блок общей памяти  с
 
      именем DATA, который выравнен по краю  страницы  с  помощью
 
      файла  возможностей  DATA.OPT.  Для  синхронизации   обмена
 
      информацией используются флаги событий:
 
      !UFO_CREATE.FOR
 
      INTEGER FUNCTION UFO_CREATE (FAB,
 
      2                            RAB,
 
      2                            LUN)
 
      !Включить СУД-32
 
      INCLUDE '($FABDEF)'
 
      INCLUDE '($RABDEF)'
 
      !Описать формальные параметры
 
      RECORD /FABDEF/ FAB
 
      RECORD /RABDEF/ RAB
 
      INTEGER LUN
 
      !Описать канал
 
      INTEGER*4 CHAN
 
      COMMON /CHANNEL/ CHAN
 
      !Описать переменную состояния
 


                                - 175 -
 
                          00152-01 97 09-1
 
 
      INTEGER STATUS
 
      !Описать системные процедуры
 
      INTEGER SYS$CREATE
 
      !Установить бит открытости для пользователя в длинном
 
      !слове блока доступа к файлу
 
      FAB.FAB$L_FOP = FAB.FAB$L_FOP .OR. FAB$M_UFO
 
      !Открыть файл
 
      STATUS = SYS$CREATE (FAB)
 
      !Читать канал из слова состояния блока доступа к файлу
 
      CHAN = FAB.FAB$L_STV
 
      !Вернуть состояние операции открытия файла
 
      UFO_CREATE = STATUS
 
      END
 
      !DATA.OPT
 
      PSECT_ATTR = DATA, PAGE
 
      !DEVICE.FOR
 
      !Определить флаги глобальной секции
 
      INCLUDE '($SECDEF)'
 
      !Маска флагов секции
 
      INTEGER SEC_MASK
 
      !Номер логического устройства для файла секции
 
      INTEGER INFO_LUN
 
      !Номер канала для файла секции
 
      INTEGER SEC_CHAN
 
      COMMON /CHANNEL/ SEC_CHAN
 
      !Длина файла для секции
 
      INTEGER SEC_LEN
 
      !Данные для файла секции
 
      CHARACTER*12 DEVICE,
 
      2            PROCESS


                                - 176 -
 
                          00152-01 97 09-1
 
 
      CHARACTER*6 TERMINAL
 
      COMMON /DATA/ DEVICE,
 
      2             PROCESS,
 
      2             TERMINAL
 
      !Расположение данных
 
      INTEGER PASS_ADDR (2),
 
      2       RET_ADDR (2)
 
      !Два общих флага событий
 
      INTEGER REQUEST_FLAG,
 
      2       INFO_FLAG
 
      DATA REQUEST_FLAG /70/
 
      DATA INFO_FLAG /71/
 
      !Процедуры, открытые для пользователя
 
      INTEGER UFO_CREATE
 
      EXTERNAL UFO_CREATE
 
      !Открыть файл секции
 
      STATUS = LIB$GET_LUN (INFO_LUN)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      SEC_MASK = SEC$M_WRT .OR. SEC$M_DZRO .OR. SEC$M_GBL
 
      !Последний адрес---первый адрес + длина последнего
 
      !элемента
 
      SEC_LEN = ( (%LOC(TERMINAL)---%LOC(DEVICE)+6+511)/512)
 
      OPEN (UNIT=INFO_LUN,
 
      2     FILE='INFO.TMP',
 
      2     STATUS='NEW,
 
      2     INITIALSIZE = SEC_LEN,
 
      2     USEROPEN = UFO_CREATE)
 
      !Освободить номер логического устройства и
 
      !картировать секцию
 
      CLOSE (INFO_LUN)


                                - 177 -
 
                          00152-01 97 09-1
 
 
      !Принять расположение данных
 
      PASS_ADDR (1) = %LOC (DEVICE)
 
      PASS_ADDR (2) = %LOC (TERMINAL)
 
      STATUS = SYS$CRMPSC (PASS_ADDR,       !Адрес секции
 
      2                    RET_ADDR,        !Картированные
 
                                            !адреса
 
      2                   ,
 
      2                    %VAL(SEC_MASK),  !Маска секции
 
      2                    'GLOBAL_SEC',    !Имя секции
 
      2                    ,,
 
      2                    %VAL(SEC_CHAN),  !Канал ввода-вывода
 
      2                    ,,,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Создать процесс
 
      STATUS = SYS$CREPRC (,
 
      2                    'GETDEVINF',     !Образ
 
      2                    ,,,,,
 
      2                    'GET_DEVICE',    !Имя процесса
 
      2                    %VAL(4),,,)      !Приоритет
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Писать данные в секцию
 
      DEVICE = '$FLOPPY1'
 
      !Взять кластер флагов событий и установить флаг
 
      STATUS = SYS$ASCEFC (%VAL(REQUEST_FLAG),
 
      2                    'CLUSTER',,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      STATUS = SYS$SETEF (%VAL(REQUEST_FLAG))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Когда GETDEVINE получит информацию, флаг будет
 
      !установлен


                                - 178 -
 
                          00152-01 97 09-1
 
 
      STATUS = SYS$WAITFR (%VAL(INFO_FLAG))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL(%VAL(STATUS))
 
      !GETDEVINF.FOR
 
      !Определить флаги секции
 
      INCLUDE '($SECDEF)'
 
      !Маска для флагов секции
 
      INTEGER SEC_MASK
 
      !Данные для файла секции
 
      CHARACTER*12 DEVICE,
 
      2            PROCESS
 
      CHARACTER*6 TERMINAL
 
      COMMON /DATA/ DEVICE,
 
      2             PROCESS,
 
      2             TERMINAL
 
      !Расположение данных
 
      INTEGER PASS_ADDR (2),
 
      2       RET_ADDR (2)
 
      !Два общих флага событий
 
      INTEGER REQUEST_FLAG,
 
      2       INFO_FLAG
 
      DATA REQUEST_FLAG /70/
 
      DATA INFO_FLAG /71/
 
      !Взять кластер флагов общих событий и ждать, пока
 
      !GBL1.FOR установит REQUEST_FLAG
 
      STATUS = SYS$ASCEFC (%VAL(REQUEST_FLAG),
 
      2                    'CLUSTER',,)
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      STATUS = SYS$WAITFR (%VAL(REQUEST_FLAG))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 


                                - 179 -
 
                          00152-01 97 09-1
 
 
      !Взять расположение данных
 
      PASS_ADDR (1) = %LOC (DEVICE)
 
      PASS_ADDR (2) = %LOC (TERMINAL)
 
      !Установить флаг записи
 
      SEC_MASK = SEC$M_WRT
 
      !Картировать секцию
 
      STATUS = SYS$MGBLSC (PASS_ADDR,      !Адрес секции
 
      2                    RET_ADDR,       !Адрес катировки
 
      2                    ,
 
      2                    %VAL(SEC_MASK) ,!Маска секции
 
      2                    'GLOBAL_SEC',,) !Имя секции
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      !Вызвать GETDVI для получения ID процесса, за которым
 
      !закреплено устройство, затем вызвать GETJPI для
 
      !получения имени процесса и имени терминала, связанных
 
      !с этим идентификатором процесса. Переменной PROCESS
 
      !присвоить имя процесса и переменной TERMINAL присвоить
 
      !имя терминала
 
      !После того, как информация находится в GLOBAL_SEC
 
      STATUS = SYS$SETEF (%VAL(INFO_FLAG))
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
 
           По умолчанию глобальная секция уничтожается, когда нет
 
      ни одного отображенного к ней образа. Такие глобальные сек-
 
      ции называются временными. Привилегия PRMGBL позволяет соз-
 
      дать   постоянную   глобальную   секцию   (установить   бит
 
      SEC$V_PERM во флаговом параметре  при  вызове  SYS$CRMPSC).
 
      Постоянная глобальная секция не уничтожается  до  тех  пор,
 
      пока не будет помечена на удаление системной служебной про-
 


                                - 180 -
 
                          00152-01 97 09-1
 
 
      цедурой SYS$DGBLSC (требует привилегии PRMGBL). После того,
 
      как постоянная секция помечена на удаление, она  становится
 
      подобна временной и уничтожается, когда нет ни одного отоб-
 
      раженного к ней образа.
 
 
 
           3.5. Сообщения внутри процесса
 
 
           Для передачи данных от выполняемого  в  данный  момент
 
      образа к образу, который будет выполняться  впоследствии  в
 
      рамках данного процесса, можно применять общие  блоки  про-
 
      цесса, локальные  флаги  событий  и  идентификаторы.  Можно
 
      использовать и логические имена, однако следует  убедиться,
 
      что используются логические имена пользовательского режима.
 
      По умолчанию процедура SYS$CRELNM создает логические  имена
 
      пользовательского режима, которые уничтожаются  с  заверше-
 
      нием создавшего их образа. Общие  блоки  процесса  наименее
 
      подвержены порче образами,  выполняющимися  между  образом,
 
      записавшим информацию, и образом, использующим ее,  поэтому
 
      именно они рекомендуются к применению.
 
           Процедуры  из   библиотеки   исполнительных   процедур
 
      LIB$RUN_PROGRAM и LIB$DO_COMMAND позволяют вызывать  после-
 
      дующий образ из текущего. Общие блоки  процесса,  локальные
 
      флаги событий, идентификаторы  и  логические  имена  режима
 
      супервизора одинаково  приемлемы  для  передачи  информации
 
      между "сцепленными"  образами,  поскольку  образ,  читающий
 
      информацию, выполняется немедленно за образом, ее  записав-
 
      шим.
 


                                - 181 -
 
                          00152-01 97 09-1
 
 
           3.5.1. Общие блоки процесса
 
 
           Для передачи данных от выполняемого образа  к  образу,
 
      который будет выполняться  впоследствии,  можно  воспользо-
 
      ваться общим блоком процесса (емкость 252 байта). Процедура
 
      LIB$PUT_COMMON позволяет записать данные в общий блок  про-
 
      цесса, а процедура LIB$GET_COMMON позволяет из этого  блока
 
      читать.
 
           Общий блок процесса создается автоматически,  не  тре-
 
      буется никаких специальных описаний. Для передачи более 252
 
      байтов данных нужно записать их в файл и передать имя этого
 
      файла с помощью общего блока процесса.
 
 
           Пример 1.
 
           Следующий  програмный  сегмент  читает  статистические
 
      данные с терминала и вводит их в двоичный файл. После того,
 
      как все статистические данные помещены  в  файл,  программа
 
      записывает имя файла в общий блок процесса и завершается:
 
      !Ввести статистику
 
      !Положить имя файла в общий блок
 
      STATUS = LIB$PUT_COMMON (FILE_NAME (1:LEN))
 
           Пример 2.
 
           Следующая программа читает имя файла из  общего  блока
 
      процесса и составляет  отчет  на  основании  статистических
 
      данных из файла:
 
      !Читать имя файла статистики из общего блока
 
      STATUS = LIB$GET_COMMON (FILE_NAME,
 
      2                        LEN)
 
      !Составить отчет .
 
           Данные в общем блоке процесса не  могут  быть  удалены
 
      или изменены без вызова LIB$PUT_COMMON. Таким образом, меж-


                                - 182 -
 
                          00152-01 97 09-1
 
 
      ду двумя образами может быть выполнено  произвольное  коли-
 
      чество образов, если гарантировано, что  не  будет  вызвана
 
      LIB$PUT_COMMON. Вызов LIB$GET_COMMON  не  меняет  данных  в
 
      общем блоке процесса.
 
           В описании LIB$PUT_COMMON и LIB$GET_COMMON [2]  указа-
 
      но, что в качестве параметра, содержащего данные,  записан-
 
      ные в общий блок процесса или прочитанные  оттуда,  исполь-
 
      зуется строка символов;  недопустимо  задавать  другой  тип
 
      данных. Однако, так как данные должны передаваться по деск-
 
      риптору,  для  передачи  несимвольных   данных   достаточно
 
      использовать встроенную функцию %DESCR.
 
           Постоянные почтовые ящики и постоянные глобальные сек-
 
      ции не уничтожаются при выходе  создавшего  их  образа,  их
 
      также можно применять для передачи информации  от  текущего
 
      образа к образу, выполняемому позднее. Однако рекомендуется
 
      использование общего блока  процесса,  поскольку  при  этом
 
      используется меньше системных ресурсов и не требуется  при-
 
      вилегий (для  создания  почтового  ящика  нужна  привилегия
 
      PRMBX, а для создания постоянной глобальной секции - приви-
 
      легия PRMGBL).
 
 
 
           3.5.2. Передача управления
 
 
           Процедуры LIB$DO_COMMAND и LIB$RUN_PROGRAM [2]  позво-
 
      ляют вызвать на выполнение следующий образ из  выполняемого
 
      в данный момент, т.е.  эти  процедуры  позволяют  закончить
 
      работу образа и передать управление следующему  образу  без
 
      возврата  на  командный  уровень  DCL.  Какую  из  процедур
 
      использовать, зависит от того, является ли следующий  образ
 
      командным образом или некомандным образом.
 


                                - 183 -
 
                          00152-01 97 09-1
 
 
           Командный образ вызывается с  помощью  соответствующей
 
      команды языка DCL, например,  команда  выполняет  командный
 
      образ, связанный с командой DCL COPY:
 
      $ COPY DATA.TMP APRIL.DAT
 
           Для передачи управления от текущего образа  командному
 
      используется  процедура  LIB$DO_COMMAND.   Если   процедура
 
      LIB$DO_COMMAND выполняется успешно, то управление  к  вызы-
 
      вающему образу не  возвращается  и  операторы,  стоящие  за
 
      LIB$DO_COMMAND не  будут  выполняться.  Например,  оператор
 
      вызывает окончание работы текущего образа и выполняет пока-
 
      занную выше команду DCL:
 
      STATUS = LIB$DO_COMMAND ('COPY DATA.TMP APRIL.DAT')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
           Для выполнения нескольких команд DCL задается  команд-
 
      ная  процедура;  например,  оператор  вызывает  прекращение
 
      работы текущего  образа  и  выполняет  командную  процедуру
 
      [STATS. TEMP] CLEANUP.COM:
 
      STATUS = LIB$DO_COMMAND ('@[STATS.TEMP]CLEANUP')
 
      IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
           Некомандный образ вызывается с командного  уровня  DCL
 
      командой RUN, например, команда RUN  выполняет  некомандный
 
      образ [STATISTICS.TEMP]TEST.EXE:
 
      $ RUN [STATISTICS.TEMP]TEST
 
           Для передачи управления из текущего образа некомандно-
 
      му  используется  процедура   LIB$RUN_PROGRAM   [2].   Если
 
      LIB$RUN_PROGRAM выполняетсяя успешно, то управление к  выз-
 
      вавшему образу не возвращается и  операторы,  следующие  за
 
      LIB$RUN_PROGRAM выпоняться не будут.  Следующий  програмный


                                - 184 -
 
                          00152-01 97 09-1
 
 
      сегмент вызывает окончание работы текущего образа  и  пере-
 
      дает         управление         некомандному         образу
 
      [STATTISTICS.TEMP]TEST.EXE на диске, принятом по умолчанию:
 
      STATUS = LIB$RUN_PROGRAM ('[STATISTICS.TEMP]TEST.EXE')
 
      IF (.NOT. STATUS)CALL LIB$SIGNAL (%VAL(STATUS))
 
      END
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 185 -
 
                          00152-01 97 09-1
 
 
                          4. Цикл подготовки
 
 
 
           Цикл подготовки - это обработка исходных текстов прог-
 
      рамм, подпрограмм и функций для превращения их в  выполняе-
 
      мую программу (или программы). Средства, применяемые в этом
 
      процессе - транслятор с языка ФОРТРАН, компоновщик, библио-
 
      текарь и отладчик.
 
           Обычно для создания и модификации  файлов,  содержащих
 
      исходные программные единицы, используется редактор EDT.
 
 
 
           4.1. Трансляция и компоновка программ
 
 
           Обычный цикл подготовки выполняемой  программы  (назы-
 
      ваемой также выполняемым образом, прикладной  системой  или
 
      подсистемой) начинается с трансляции  исходных  программных
 
      единиц в об'ектные модули и включения этих  модулей  (кроме
 
      модуля головной программы) в  об'ектную  библиотеку.  Затем
 
      происходит подготовка  этих  модулей  в  выполняемый  образ
 
      (обычно не требуется помещать об'ектные модули в  библиоте-
 
      ку. Их можно компоновать непосредственно). Рис. 4 иллюстри-
 
      рует цикл подготовки.
 


                                - 186 -
 
                          00152-01 97 09-1
 
 
 
                Процесс подготовки выполняемого образа
 
                   ------------------------------
                   !                            !
                   !Исходные программные единицы!
                   !                            !
                   ------------------------------
                               !
                               !команда FORTRAN
                               V
                   -------------------    --------------------
                   ! Об'ектные модули !   ! Другие об'ектные !
                   !                  !   !     модули       !
                   --------------------   --------------------
                        !   !                       !
                        !   -------------------------
                        !               !
                        !               !команда LIBRARY
                        !               V
                        !    ------------------------
                        !    ! Библиотека об'ектных !
                        !    !      модулей         !
                        !    ------------------------
                        !               !
                        -----------------
                                !
                                !команда LINK
                                V
                      ---------------------
                      ! Выполняемый образ !
                      ---------------------
 
 
                                Рис. 4
 
 
 
           4.1.1. Создание исходной программы
 
 
           Исходные программы создаются в виде файлов с типом FOR
 
      при соблюдении следующих правил:
 
           1) каждый оператор  языка  фортран  должен  начинаться
 
      после первого  знака  табуляции  на  новой  строке  (нажать
 
      <RETURN> и <TAB>), иначе будет ошибка. В редакторе  EDT  не
 
      рекомендуется менять установку первого знака табуляции (EDT
 
      заменяет знак табуляции на 8 пробелов, если позиция табуля-
 
      тора не кратна 8);
 
           2) метки помещаются (например, у оператора  FORMAT)  в
 


                                - 187 -
 
                          00152-01 97 09-1
 
 
      первой позиции новой строки; за меткой идет знак табуляции;
 
           3) пробелы применяются для улучшения  читаемости.  Они
 
      не влияют на процесс перевода  программы  в  об'ектный  код
 
      (если эти пробелы следуют после первого символа табуляции).
 
      Например, можно писать все слитно, а можно вставлять пробе-
 
      лы между словами, числами и  идентификаторами;  предложение
 
      DO WHILE (STATUS) значит то же, что и DOWHILE(STATUS);
 
           4) допускается продолжение оператора  на  произвольное
 
      число строк. Каждая строка  продолжения  должна  начинаться
 
      после знака табуляции с любой цифры, кроме 0.  Не  рекомен-
 
      дуется растягивать строку далее позиции 80;
 
           5)  разрешается  комментировать  программу,   поместив
 
      звездочку или букву 'C' в начале строки перед знаком  табу-
 
      ляции; оставив строку пустой; поместив восклицательный знак
 
      в любой позиции строки - все, что расположено после воскли-
 
      цательного знака, будет воспринято как  комментарий.  Реко-
 
      мендуется применять восклицательный знак  и  пустые  строки
 
      для об'яснений, а букву 'C' и звездочку  -  для  выключения
 
      предложений  из  программы  (предложение  из  программы  не
 
      вычеркивается, но не транслируется).
 
 
           Пример.
 
           Файл  GET_FROM.FOR  с  исходным  текстом  подпрограммы
 
      GET_FROM_BUFF:
 
              SUBROUTINE GET_FROM_BUFF (BUF,
 
              2                         PTR,
 
              2                         SIZ,
 
              2                         CURLIN,
 
              2                         LINE,    !Возвращаемое
 
                                                 !значение
 


                                - 188 -
 
                          00152-01 97 09-1
 
 
              2                         LINE_LEN)!Возвращаемое
 
                                                 !значение
 
              !Принять строку из буфера
 
              !Описать параметры
 
              CHARACTER BUF (204800) !Буфер
 
              INTEGER PTR (10240),   !Указатели строк
 
              2       SIZ (10240),   !Размеры строк
 
              2       CURLIN         !Номер строки, подлежащей
 
                                     !извлечению
 
              CHARACTER LINE (80)    !Строка для извлечения
 
              INTEGER LINE_LEN       !Длина извлекаемой строки
 
              !Описание локальных данных
 
              INTEGER OFF,          !Смещение в буфере для посим-
 
              2       I             !вольного извлечения строки
 
              !Принять строку из буфера
 
              OFF=PTR (CURLIN)-1
 
              LINE_LEN=SIZ (CURLIN)
 
              DO I=1, LINE_LEN
 
              LINE (I)=BUF (OFF+I)
 
              END DO
 
              END                     !Конец подпрограммы  .
 
 
 
           4.1.2. Трансляция программы
 
 
           Команда  FORTRAN  транслирует  исходную  программу   в
 
      об'ектные модули. В простейшей форме этой команде требуется
 
      указать только имя файла, содержащего исходную  программную
 
      единицу (или единицы). Тип  исходных  файлов  по  умолчанию
 
      есть FOR, а об'ектных файлов - OBJ.
 


                                - 189 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Команда FORTRAN транслирует программную единицу  (еди-
 
      ницы) из файла с именем REPORT.FOR в принятом по  умолчанию
 
      каталоге и создает файл REPORT.OBJ, содержащий результирую-
 
      щий модуль (модули):
 
      $ FORTRAN REPORT
 
           Если REPORT.FOR содержит одну программную  единицу  по
 
      имени WRITE_REPORT, то REPORT.OBJ содержит  один  об'ектный
 
      модуль с именем WRITE_REPORT. Если REPORT.FOR содержит  две
 
      программных единицы с именем WRITE_REPORT и CALC_SUMMARIES,
 
      то REPORT.OBJ будет содержать два об'ектных модуля  с  теми
 
      же именами, но типом OBJ. Имена программных единиц  и  фай-
 
      лов - понятия разные.
 
           За  один  прием  можно  оттранслировать  более  одного
 
      исходного файла. Если отделить имена  файлов  запятыми,  то
 
      результирующие об'ектные модули  будут  помещены  в  разные
 
      файлы с  именами  исходных  файлов.  Если  разделить  имена
 
      исходных файлов знаками плюс, то  результирующие  об'ектные
 
      модули будут помещены в один об'ектный файл с именем перво-
 
      го исходного файла.
 
           Пример 1.
 
           Получение двух об'етных файлов с именAми REPORT.OBJ  и
 
      CALC.OBJ:
 
      $ FORTRAN REPORT, CALC
 
           Пример 2.
 
           Получение одного об'ектного файла с именем REPORT.OBJ:
 
      $FORTRAN REPORT+CALC
 


                                - 190 -
 
                          00152-01 97 09-1
 
 
           4.1.2.1. Разработка систем
 
 
           При трансляции программных единиц для системы, находя-
 
      щейся в стадии разработки, в команду FORTRAN следует  вклю-
 
      чать следующие квалификаторы:
 
           /DEBUG  -  к  каждому  об'ектному  модулю  добавляется
 
      информация о локальных идентификаторах, имя  входной  точки
 
      (имя программы  или  подпрограммы,  указывающее  на  первую
 
      выполняемую строку)  и  номера  строк  (связывая  строки  с
 
      пораждаемым ею кодам). При прогоне программы эта информация
 
      позволяет проводить отладку  в  терминах  имен  программ  и
 
      подпрограмм, имен переменных и номеров строк программ;
 
           /CHECK - добавляет к каждому об'ектному модулю  инфор-
 
      мацию для отслеживания выхода индекса за  границы  массива,
 
      арифметического переполнения сверху и снизу;
 
           /NOOPTIMIZE  -  прEпятствует  оптимизации   об'ектного
 
      кода. Обычно оптимизация так меняет об'ектные  модули,  что
 
      они уже не отражают  исходную  программную  единицу  в  том
 
      виде, в котором она написана. В частности, при  оптимизации
 
      для некоторых математических операций используются не пере-
 
      менные, а регистры процессора, так что анализ переменных во
 
      время работы не дает верного результата;
 
           /LIST - записывает листинг в файл с  именем  исходного
 
      текста (если не указано другое) и  типом  файла  LIS.  Этот
 
      файл может быть выведен на печать или на  дисплей.  Листинг
 
      включает исходные операторы каждой  программной  единицы  с
 
      номерами строк, а также типы данных и адреса (по  отношению
 
      к началу программы) переменных. Листинг полезен при  отлад-
 
      ке. Можно уничтожить листинг,  когда  он  больше  не  будет
 
      нужен, поскольку он требует большого об'ема внешней памяти.
 


                                - 191 -
 
                          00152-01 97 09-1
 
 
      листинг можно  получить,  не  создавая  об'ектных  модулей,
 
      задав квалификаторы /LIST и /NOOBJECT в команде FORTRAN.
 
 
           Пример.
 
           Транслируется программная единица из файла  REPORT.FOR
 
      для целей разработки. В  результате  трансляции  получается
 
      неоптимизированный об'ектный код с полной отладочной инфор-
 
      мацией в файле с именем REPORT.OBJ и листинг в файле с име-
 
      нем REPORT.LIS:
 
      $ FORTRAN/DEBUG/CHECK/NOOPTIMIZATION/LIST REPORT
 
 
 
           4.1.2.2. Создание систем
 
 
           Когда программа отлажена и необходимо получить об'ект-
 
      ные модули для  ее  эксплуатации,  следует  оттранслировать
 
      исходные файлы без квалификаторов /DEBUG, /CHECK  (отладоч-
 
      ная  информация  больше  не  нужна)  и  без   квалификатора
 
      /NOOPTIMIZE (рабочие характеристики желательно  оптимизиро-
 
      вать). Для получения  итогового  листинга  следует  указать
 
      квалификатор /LIST, например:
 
      $ FORTRAN/LIST REPORT
 
 
 
           4.1.2.3. Ошибки
 
 
           О наличии синтаксических ошибок сп фортран указывает в
 
      конце трансляции в  сообщениях,  начинающихся  с  приставки
 
      %FORT. В эти сообщения включаются номер строки оператора  и
 
      часть текста, содержащего ошибку. Эти  же  сообщения  появ-
 
      ляются в листинге.
 
           Часто сообщение об ошибке не указывает верно ее причи-
 
      ну, а только точно указывает на  ее  место.  В  большинстве
 


                                - 192 -
 
                          00152-01 97 09-1
 
 
      случаев ошибка вызвана простым нарушением правил синтаксиса
 
      или опечаткой. Обычные ошибки таковы:
 
           1) нет скобок. Опущена скобка в конце списка  парамет-
 
      ров, что порождает ошибку MISSDEL (отсутствие знака  опера-
 
      ции или разделителя);
 
           2) в строке продолжения нет цифры. Отсутствие цифры  в
 
      строке продолжения порождает самые разные сообщения в зави-
 
      симости от контекста,  так  как  транслятор  языка  ФОРТРАН
 
      пытается интерпретировать эту строку  как  новый  оператор.
 
      Следует помнить, что цифра ноль не годится в качестве  ука-
 
      зателя продолжения. Цифра продолжения в строках, где она не
 
      нужна, расширение строки сверх 80 символов или  использова-
 
      ние пробелов вместо первого знака табуляции также дает неп-
 
      редсказуемые результаты;
 
           3) отсутствие запятой. Опущенная запятая в списке опе-
 
      рандов обычно приводит к ошибке EXTCHAFOL  (лишние  символы
 
      после разрешенного оператора);
 
           4) нет символа комментария. Строка-комментарий  должна
 
      начинаться (перед знаком табуляции) со звездочки, буквы 'C'
 
      или восклицательного знака. Отсутствие такого знака  обычно
 
      приводит к ошибке MISSDEL;
 
           5) неверное условие. Условие в  операторе  IF  или  DO
 
      WHILE должно быть заключено в скобки. Знак  условия  должен
 
      иметь вид .EQ., .LE. и т.д., а не быть знаком равенства или
 
      знаком меньше или равно. Каждая открывающая  скобка  должна
 
      иметь соответствующую закрывающую.  Нарушение  этих  правил
 
      обычно приводит к ошибке MISSDEL, NOPATH (нет пути к опера-
 
      тору) или  INVCONSTR  (неправильный  управляющий  оператор,
 
      использующий ELSE IF, ELSE или END IF);
 
           6) отсутствие THEN в блоке IF. Отсутствие THEN в блоч-


                                - 193 -
 
                          00152-01 97 09-1
 
 
      ном операторе IF обычно приводит к ошибкам MISSDEL,  NOPATH
 
      и INVCONSTR;
 
           7) отсутствие END для IF и  DO.  Отсутствие  оператора
 
      END IF для блока IF или оператора END DO для  оператора  DO
 
      приводит к ошибке OPEDOLOOP (незакрытый цикл  DO  или  блок
 
      IF).
 
           Простой способ анализа ошибок трансляции  -  прочитать
 
      файл листинга с помощью EDT (EDIT/READ) с  поиском  каждого
 
      появления %FORT. После определения ошибок и способа их исп-
 
      равления отредактировать исходный файл с программой на язы-
 
      ке фортран (не файл  листинга)  для  исправления  ошибок  и
 
      перетранслировать его. Если ошибки многочисленны и  сложны,
 
      нужно распечатать файл листинга для "кабинетной работы".
 
           Ошибка, не обнаруживаемая транслятором, наличие  кото-
 
      рой всегда следует проверять  -  это  неправильное  задание
 
      имен переменных. Когда транслятор находит имя,  неописанное
 
      явно, он строит переменную, тип которой задан неявным опре-
 
      делением по первой букве имени.
 
 
           Пример.
 
      INTEGER CURLIN !Описание переменной
 
      !
 
      ! Продолжение описаний
 
      !
 
      оFF=PTR (CURLIN)-1
 
      LINE_LEN=SIZ (CURLINE) !Неправильное использование CURLIN
 
      DO I=1, LINE_LEN       ! Лишняя буква 'E'
 
        LINE (I)=BUF (OFF+I)
 
      END DO
 
           Еще один способ (кроме неправильного написания) задать
 


                                - 194 -
 
                          00152-01 97 09-1
 
 
      имя переменной неверно - это опустить требуемую пунктуацию.
 
      Например, при  описаниии  функции  GET_FROM_BUFF  в  списке
 
      параметров опущена  запятая,  отчего  СП  ФОРТРАН  образует
 
      новую переменную CURLINLINE (пробелы и комментарии не могут
 
      служить разделителями):
 
      INTEGER FUNCTION GET_FROM_BUFF (BUF,
 
      2                               PTR,
 
      3                               SIZ,
 
      4                               CURLIN  !Пропущена запятая
 
      5                               LINE,
 
      6                               LINE_LEN)
 
           Можно запретить неявное описание переменных,  поставив
 
      среди невыполняемых операторов программы оператор  IMPLICIT
 
      NONE. Тогда  указанные  ошибки,  связанные  с  неправильным
 
      заданием имен, будут обнаружены во время трансляции.
 
           Если не используется оператором IMPLICIT NONE, то нуж-
 
      но просматривать список переменных  (VARIABLE)  и  массивов
 
      (ARRAY) в конце листинга, чтобы увидеть, нет ли там  неожи-
 
      данных имен. В этой секции помещаются  переменные,  опреде-
 
      ленные транслятором языка фортран по принципу умолчания.
 
 
 
           4.1.3. Об'ектные библиотеки
 
 
           В любой большой разработке об'ектные  модули  подпрог-
 
      раммы следует хранить в  об'ектной  библиотеке  (называемой
 
      также библиотекой  об'ектных  модулей).  Наличие  об'ектной
 
      библиотеки не только уменьшает  число  файлов,  с  которыми
 
      приходится работать, но и сильно упрощает процесс компонов-
 
      ки. Для начального создания  об'ектной  библиотеки  исполь-
 
      зуется команда LIBRARY с квалификатором  /CREATE  и  именем
 


                                - 195 -
 
                          00152-01 97 09-1
 
 
      создаваемой библиотеки.
 
 
           Пример.
 
           Создается  библиотека  в  файле  с  именем  INCOME.OLB
 
      (OLB - тип файла, принятый по умолчанию для об'ектной  биб-
 
      лиотеки):
 
      $ LIBRARY/CREATE INCOME
 
           Для добавления или замены модулей в библиотеке исполь-
 
      зуется команда LIBRARY с квалификатором /REPLACE, за  кото-
 
      рым следует имя библиотеки (первый параметр) и имя  (имена)
 
      файла (файлов), содержащих модуль или модули (второй  пара-
 
      метр). После помещения об'ектного модуля или модулей в биб-
 
      лиотеку об'ектный файл можно уничтожить. Например, добавле-
 
      ние об'ектных файлов из  модуля  GETSTATS.OBJ  в  об'ектную
 
      библиотеку INCOME.OLB (об'ектные файлы уничтожаются) выпол-
 
      няется командами:
 
      $ LIBRARY/REPLACE INCOME GETSTATS
 
      $ DELETE GETSTATS.OBJ;*
 
           Содержание  об'ектной  библиотеки  можно   просмотреть
 
      командой  LIBRARY  с   квалификатором   /LIST.   Ограничить
 
      демонстрацию  содержания  можно  с  помощью   квалификатора
 
      /ONLY. Например, просмотр всех модулей из INCOME.OLB, начи-
 
      нающихся с GET, выполняется командой:
 
      $ LIBRARY/LIST/ONLY=GET* INCOME
 
           Для уничтожения об'eктного модуля применяется квалифи-
 
      катор /DELETE, а для воссоздания об'ектного файла -  квали-
 
      фикатор /EXTRACT. Если удаляется много модулей, то  библио-
 
      теку следует уплотнить  (/COMPRESS)  и  почистить  (команда
 
      PURGE). Квалификаторы /ONLY,  /DELETE  и  /EXTRACT  требуют
 
      указания имени модулей (а не  имени  файлов)  и  эти  имена
 


                                - 196 -
 
                          00152-01 97 09-1
 
 
      задаются как значения квалификаторов, а не параметров.
 
 
 
           4.1.4. Компоновка и выполнение программ
 
 
           Команда LINK собирает об'ектные модули  в  выполняемый
 
      образ, окончательно уточняя положение глобальных  идентифи-
 
      каторов, таких, как имена входных точек, имена общих облас-
 
      тей и других переменных, описанных как  внешние.  Об'ектные
 
      модули можно задать, указав файлы, в которых они существуют
 
      или библиотеки, их содержащие (имя библиотеки должно сопро-
 
      вождаться квалификатором /LIBRARY). Обычно  называют  файл,
 
      содержащий головную программу, а за ним библиотеку,  содер-
 
      жащую подпрограммы. Например, если файл INCOME.OBJ содержит
 
      головную программу, а файл INCOME.OLB  -  подпрограммы,  то
 
      команда LINK будет такой:
 
      $ LINK INCOME,INCOME/LIBRARY
 
           Для разработки систем обычно добавляется  квалификатор
 
      /DEBUG. Этот квалификатор добавляет к образу всю информацию
 
      об идентификаторах и номерах строк, добавленную к об'ектным
 
      модулям, плюс информацию о  глобальных  идентификаторах,  и
 
      вынуждает образ при выполнении находиться  под  управлением
 
      отладчика.
 
 
           Пример.
 
      $ LINK/DEBUG INCOME, INCOME/LIBRARY
 
           Если образ вызывается с помощью команды RUN, то отлад-
 
      чик может быть отключен заданием  RUN/NODEBUG.  Если  образ
 
      вызывается в качестве команды языка DCL, то для  отключения
 
      отладчика  его  надо  перекомпоновать   без   квалификатора
 
      /DEBUG. Если программа выполняется без  отладчика,  то  его
 
      можно вызвать, нажав CTRL/Y и введя команду DEBUG:


                                - 197 -
 
                          00152-01 97 09-1
 
 
      $ RUN INCOME
 
      <CTRL/Y>
 
      $ DEBUG
 
           Для отлаженных систем желательно не только  подавление
 
      отладчика (отказом от  задания  квалификатора  /DEBUG),  но
 
      также подавление демонстрации информации обратного  просле-
 
      живания заданием /NOTRACEBACK. Информация обратного просле-
 
      живания сообщает о том, в какой строке каждого модуля прои-
 
      зошла ошибка этапа прогона.  Однако  по  окончанию  отладки
 
      системы эта информация перестает быть необходимой и являет-
 
      ся скорее мусором в тех случаях, когда образ после  сообще-
 
      ния об ошибке может продолжать выполнение (т.е. важно  само
 
      сообщение, а информация обратного прослеживания не нужна).
 
 
           Пример.
 
      Компоновка отлаженной системы:
 
      $ LINK/NOTRACEBACK INCOME, INCUME/LIBRARY
 
           Если  выполняемый  образ  устанавливается  в  качестве
 
      команды DCL с использованием описаний командного языка,  то
 
      для выполнения образа требуется только  ввести  его  имя  в
 
      сопровождении  квалификаторов   и   параметров.   Например,
 
      INCOME.EXE выполняется с помощью вызова  команды  INCоME  с
 
      квалификатором /ENTER:
 
      $ INCOME/ENTER
 
           Если образ не установлен в качестве  команды  DCL,  то
 
      его можно выполнить, указав его имя  в  качестве  параметра
 
      команды RUN:
 
      $ RUN INCOME
 


                                - 198 -
 
                          00152-01 97 09-1
 
 
           4.1.5. Привилегированные программы
 
 
           Если для выполнения программы нужна определенная  при-
 
      вилегия, то нужно установить эту программу как привилегиро-
 
      ванный образ. Если программа установлена  как  привилегиро-
 
      ванный образ, то она имеет  заданные  привилегии,  исключая
 
      этим необходимость для пользователя иметь  эти  привилегии.
 
      Чтобы избежать проблем защиты информации, следует  отменить
 
      демонстацию привилегированным образом информации  обратного
 
      слежения; для этого перед установкой он должен быть скомпо-
 
      нован с квалификатором /NOTRACEBACK команды LINK.
 
           Для установки программы в качестве  привилегированного
 
      образа необходимо сделать следующее:
 
           1) использовать  команду  диалогового  языка  DCL  SET
 
      PROCESS/PRIVILEGE=CMKRNL, чтобы получить привилегию  CMKRNL
 
      (требуется для использования обслуживающей программы  уста-
 
      новления);
 
           2) ввести INSTALL для вызова интерактивной обслуживаю-
 
      щей программы установления;
 
           3) после появления подсказки INSTALL>  ввести  CREATE,
 
      полную спецификацию файла, содержащего выполняемую програм-
 
      му  (тип   файла   по   умолчанию   EXE)   и   квалификатор
 
      /PRIVILEGE=(привилегия), где "привилегия" - список  требуе-
 
      мых програмной привилегий;
 
           4) нажать <RETURN>. Обслуживающая программа  установки
 
      установит программу как  привилегированный  образ  и  снова
 
      выдаст подсказку INSTALL>;
 
           5) для выхода ввести EXIT;
 
           6) снять  привилегию  CMKRNL  с  помощью  команды  SET
 
      PROCESS/PRIVILEGE=NOCMKRNL.
 


                                - 199 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Установка выполняемого образа $DISK1:[INCOME]GET_STATS
 
      с привилегией BYPASS:
 
      $ SET PROCESS/PRIVILEGE=CMKRNL
 
      $ INSTALL
 
      INSTALL>CREATE $DISK1:[INCOME]GET_STATS/PRIVILEGES=(BYPASS)
 
      INSTALL>EXIT
 
      $ SET PROCESS/PRIVILEGE=NOCMKRNL
 
           Диск, содержащий установленный образ,  не  может  быть
 
      демонтирован, пока установленный образ  не  уничтожен.  Для
 
      уничтожения установленного образа вызывается  обслуживающая
 
      программа установки и вводится DELETE в сопровождении  пол-
 
      ной спецификации файла. Для выхода используется  подкоманда
 
      EXIT.
 
 
 
           4.1.6. Построение программ
 
 
           Следует обратить  внимание  на  поддержание  исходных,
 
      об'ектных, библиотечных и выполняемых файлов на одном и том
 
      же уровне разработки. Так, например, не хотелось бы, сделав
 
      изменения в исходной программе и получив новый образ, впос-
 
      ледствии потерять связанный с этим образом исходный  текст.
 
      С этой целью следует содержать определенные  наборы  исход-
 
      ных, об'ектных, библиотечных и выполняемых файлов  в  одном
 
      каталоге.
 
           Можно сэкономить много труда и времени, создав команд-
 
      ную процедуру для трансляции и компоновки модулей и  других
 
      подготовительных операций.  Командные  процедуры  исключают
 
      ошибки, гарантируя правильное введение всех нужных  команд,
 
      стандатизируют подготовительные операции и упрощают  введе-
 


                                - 200 -
 
                          00152-01 97 09-1
 
 
      ние команд. Однако необходимо писать процедуры аккуратно  и
 
      тщательно их тестировать.
 
 
 
           4.1.6.1. Процесс построения
 
 
           Обычно требуется оттранслировать программную  единицу,
 
      получивщийся об'ектный модуль (если он не  головной)  вклю-
 
      чить в библиотеку и создать с  помощью  компоновщика  новый
 
      выполняемый образ, обновляя старые файлы и уничтожая ненуж-
 
      ные. Необходимо убедиться, что к командам  FORTRAN  и  LINK
 
      добавлены соответствующие квалификаторы, зависящие от того,
 
      строится программа окончательно, или  для  отладки,  или  с
 
      квалификатором /TRACEBACK (без DEBUG).
 
 
           Пример.
 
           Командная процедура INCOME.COM, используя параметр  P2
 
      для управления, сочетает все упомянутые действия для выпол-
 
      няемой программы, называемой INCOME и содержащейся в  ката-
 
      логе $DISK1:[DEV.INCOME]:
 
      $ ! Транслирует и компонует программную единицу для INCOME
 
      $ ! два параметра: P1 = имя файла программной единицы
 
      $ !                P2 = D-отладка (система разрабатывается)
 
      $ !                     T-обратное слежение (без отладки)
 
      $ !                     P-окончательное построение
 
      $ !
 
      $ SET DEFAULT $DISK1:[DEV.INCOME]
 
      $ ! Квалификаторы для окончательного построения
 
      $ QUALS = "/LIST"
 
      $ ! Квалификаторы для отладки или обратного прослеживания
 
      $ IF (P2 .EQS. "D") .OR. (P2 .EQS. "T") THEN -
 
        QUALS = "/DEBUG/CHECK/NOOPTIMIZE/LIST"


                                - 201 -
 
                          00152-01 97 09-1
 
 
      $ ! Трансляция
 
      $ FORTRAN'QUALS' 'P1'
 
      $ ! Если модуль головной, то в библиотеку не помещать
 
      $ IF P1 .EQS. "INCOME" THEN GOTO LINK
 
      $ ! Поместить модуль подпрограммы в библиотеку
 
      $ LIBRARY/REPLACE INCOME 'P1'
 
      $ DELETE 'P1'.OBJ;*
 
      $LINK:
 
      $ ! Если компоновка для окончательной системы
 
      $ IF P2 .EQS. "P" THEN LINK/NOTRACE
 
      $ ! Если компоновка для отладки
 
      $ IF P2 .EQS. "D" THEN LINK/DEBUG INCOME,INCOME/LIBRARY
 
      $ ! Если компоновка для обратного прослеживания
 
      $ IF P2 .EQS. "T" THEN LINK INCOME,INCOME/LIBRARY
 
      $ ! Чистка старых файлов
 
      $ PURGE
 
           Для построения новой отлаживаемой системы,  основанной
 
      на модификациях  программной  единицы  GET_STATS  из  файла
 
      GET_STATS.FOR вводится команда:
 
      $ @INCOME GET_STATS D
 
 
 
           4.1.6.2. Целостность системы
 
 
           Если уже есть программа, работающая в главном каталоге
 
      построения, то следует избегать внесения в  нее  изменений,
 
      после которых она, возможно, работать  перестанет.  В  этой
 
      связи хорошей практикой будет при написании новых программ-
 
      ных единиц или модификации старых строить новые выполняемые
 
      образы в другом каталоге до  тех  пор,  пока  не  получится
 
      программа, которая работает стабильно. В этот момент  можно
 


                                - 202 -
 
                          00152-01 97 09-1
 
 
      скопировать новую или модифицированную программу в  каталог
 
      построения и там построить новый выполняемый образ.  Нельзя
 
      просто копировать образ в каталог  построения,  т.к.  файлы
 
      образа и исходного текста не будут друг другу  соответство-
 
      вать. При построении в другом каталоге  используются  копии
 
      других об'ектных модулей из каталога построения.  Например,
 
      последовательность  команд   модифицирует   исходный   файл
 
      GETSTATS.FOR,  строит  новые  образы  INCOME   в   каталоге
 
      [MY.INCOME], пока не получится хороший образ,  затем  копи-
 
      рует GETSTATS.FOR в основной каталог  построения  и  строит
 
      образ там:
 
      $ SET DEFAULT $DISK1:[MY.INCOME]
 
      $ COPY [DEV.INCOME]GETSTATS.FOR *
 
      $ EDIT GETSTATS.FOR
 
      !
 
      ! Сеанс редактирования
 
      !
 
      $ @MYINCOME GETSTATS D ! SEE BELOW FOR MYINCOME.COM
 
      $ INCOME/ENTER
 
      !
 
      ! Сеанс отладки INCOME.EXE
 
      !
 
      ! Предшествующие этапы повторяются до получения правильного
 
      ! образа
 
      $ COPY GETSTATS.FOR [DEV.INCOME]*
 
      $ SET DEFAULT $DISK1:[DEV.INCOME]
 
      $ @INCOME GETSTATS D
 
           Командная  процедура  MYINCOME.COM  подобна  командной
 
      процедуре  INCOME.COM,  но  строит  образ  в   [MY.INCOME],
 
      используя заданную командную единицу из [MY.INCOME]. Другие


                                - 203 -
 
                          00152-01 97 09-1
 
 
      об'ектные модули  берутся  из  [DEV.INCOME].  По  умолчанию
 
      образ, созданный по команде LINK, получает имя первого фай-
 
      ла, указанного в команде.  Поэтому,  если  транслируется  и
 
      компонуется  библиотечный  модуль  из  [MY.INCOME],   нужно
 
      использовать квалификатор /EXECUTABLE команды  LINK,  чтобы
 
      явно указать новый выполняемый образ [MY.INCOME]INCOME.
 
 
           Пример.
 
           Командная процедура $DISK1:[MY.INCOME]MYINCOME.COM:
 
      $ ! Трансляция и компоновка программы в моей области
 
      $ ! Два параметра: P1 = имя файла программной  единицы
 
      $ !                P2 = D-отладка (система в стадии раз-
 
      $ !                       работки)
 
      $ !                     T-обратное слежение (без отладки)
 
      $ !                     P-окончательный вариант системы
 
      $ !
 
      $ SET DEFAULT $DISK1:[MY.INCOME]
 
      $ ! Квалификаторы для окончательного варианта
 
      $ IF (P2 .NES. "P") THEN GOTO DEBUG
 
      $ FORT_QUALS = "/LIST"
 
      $ LINK_QUALS = "/NOTRACE"
 
      $DEBUG:
 
      $ ! Квалификаторы для отладки
 
      $ IF (P2 .NES. "D") THEN GOTO TRACEBACK
 
      $ FORT_QUALS = "/DEBUG/CHECK/NOOPTIMIZE/LIST"
 
      $ LINK_QUALS = "/DEBUG"
 
      $TRACEBACK:
 
      $ ! Квалификаторы для обратного слежения
 
      $ IF (P2 .NES. "T") THEN GOTO END_QUALS
 
      $ FORT_QUALS = "/DEBUG/CHECK/NOOPTIMIZE/LIST"
 


                                - 204 -
 
                          00152-01 97 09-1
 
 
      $ LINK_QUALS = "/TRACE"
 
      $END_QUALS:
 
      $ ! Трансляция
 
      $ FORTRAN'FORT_QUALS' 'P1'
 
      $ ! Для головной программы нужна другая последовательность
 
      $ ! компоновки
 
      $ IF (P1 .EQS. "INCOME") THEN GOTO MAIN
 
      $ ! Компоновка подпрограммы
 
      $ LINK/EXECUTABLE=[]INCOME'LINK_QUALS'-
 
        [DEV.INCOME]INCOME,-        ! Головная программа
 
        []'P1',-                    ! Новая программная единица
 
        [DEV.INCOME]INCOME/LIBRARY  ! Библиотека
 
      $ GOTO PURGE
 
      $MAIN:
 
      $ ! Компоновка головной программы
 
      $ LINK'LINK_QUALS'-
 
        INCOME,-                    ! Новая головная программа
 
        [DEV.INCOME]INCOME/LIBRARY  ! Библиотека
 
      $PURGE:
 
      $ ! Чистка старых файлов
 
      $ PURGE
 
 
 
           4.1.6.3. Профилактическое копирование
 
 
           Если  над  разработкой  программы  работают  несколько
 
      человек, то следует принять меры против того, чтобы один из
 
      них затирал изменения, сделанные другим. Например,  татьяна
 
      копирует GETSTATS.FOR из основного  каталога  построения  в
 
      свою собственную область, а  через  несколько  минут  елена
 
      копирует ту же программную единицу в свою область. Обе вно-
 


                                - 205 -
 
                          00152-01 97 09-1
 
 
      сят изменения в свои копии  программ,  копируют  обратно  в
 
      каталог построения и проводят  построение.  Последний,  кто
 
      проводит построение, уничтожит изменения, внесенные другим.
 
      Такие конфликты  можно  решить  словесно,  установив  право
 
      собственности одного человека на каждую программу или расп-
 
      ределив последовательность работ. Однако гораздо безопаснее
 
      автоматизировать этот процесс таким образом, чтобы с каждой
 
      программной единицей мог одновременно работать только  один
 
      человек.
 
 
           Пример 1.
 
           Командная процедура копирует  программную  единицу  из
 
      основного каталога построения в принятый по умолчанию ката-
 
      лог и помещает информацию в  специальный  файл  в  каталоге
 
      построения (CHECKOUT.DAT, который надо перед использованием
 
      командной процедуры создать как пустой); информация  указы-
 
      вает, что программная единица вынесена. Процесс копирования
 
      обрывается, если программная единица уже скопирована ранее:
 
      $ ! Вынесение программной единицы
 
      $ ! Два параметра: P1 = имя файла программной единицы
 
      $ !                P2 = имя пользователя, выносящего про-
 
      $ !                     грамму
 
      $ !
 
      $ ON ERROR THEN EXIT ($STATUS)
 
      $ !
 
      $ ! Формат файла, регистрирующего вынесение
 
      $ ! Имя единицы  Имя пользователя  Дата
 
      $ !
 
      $ ! Читать записи из файла CHECKOUT.DAT,
 
      $ ! пока не будет обнаружена искомая программная единица
 


                                - 206 -
 
                          00152-01 97 09-1
 
 
      $ ! или не будет найден конец файла
 
      $ OPEN CHECKOUT-        ! Открыть на чтение
 
        $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ MODULE = ""           ! Инициализировать имя программы
 
      $READLOOP:
 
      $ READ/END_OF_FILE = ENDREADLOOP-
 
        CHECKOUT CHECKOUT_LINE
 
      $ ! Извлечь имя программы и сравнить с параметром 1;
 
      $ MODULE_LEN-F$LOCATE (" ", CHECкOUT_LINE)
 
      $ MODULE=F$EXтRACT (0, MODULE_LEN, CHECKOUT_LINE)
 
      $ ! Прекратить, если найден параметр 1
 
      $ IF P1 .NES. MODULE THEN GOTO READLOOP
 
      $ENDREADLOOP:
 
      $ CLOSE CHECKOUT
 
      $ !
 
      $ ! Если программная единица уже вынесена, выдать сообщение
 
      $ IF P1 .NES. MODULE THEN GOTO ENDALREADY
 
      $ WRITE SYS$OUTPUT "программная единица уже вынесена"
 
      $ WRITE SYS$OUTPUT CHECKOUT_LINE
 
      $ EXIT
 
      $ENDALREADY:
 
      $ !
 
      $ ! Иначе вынести ее
 
      $ ! Копировать исходный файл в каталог по умолчанию
 
      $ COPY $DISK1:[DEV.INCOME]'P1'.FOR *
 
      $ !При ошибке закрыть файлы регистрации вынесений до выхода
 
      $ ON ERROR THEN GOTO EXIT
 
      $ ! Сделать новую копию файла регистрации вынесений
 
      $ OPEN OLDOUT $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ OPEN/WRITE NEWOUT $DISK1:[DEV.INCOME]CHECKOUT.DAT


                                - 207 -
 
                          00152-01 97 09-1
 
 
      $WRITELOOP:
 
      $ READ/END_OF_FILE=ENDWRITELOOP OLDOUT CHECKOUT_LINE
 
      $ WRITE NEWOUT CHECKOUT_LINE
 
      $ GOTO WRITELOOP
 
      $ENDWRITELOOP:
 
      $ ! Добавить новую запись
 
      $ CHECKOUT_LINE=P1 + " " + P2 + " " + F$TIME()
 
      $ WRITE NEWOUT CHECKOUT_LINE
 
      $ ! Закрыть файлы, убрать старый файл и выйти
 
      $EXIT:
 
      $ STATUS = $STATUS
 
      $ CLOSE OLDOUT
 
      $ CLOSE NEWOUT
 
      $ PURGE $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ EXIT (STATUS)
 
           Пример 2.
 
           Командная процедура вносит программную единицу обратно
 
      в каталог построения, копируя ее из каталога по умолчанию и
 
      уничтожая соответствующую запись в CHECKOUT.DAT. Исключения
 
      происходят, если программная единица вынесена другим  поль-
 
      зователем (процесс  внесения  обравается)  или  программная
 
      единица не вынесена (выдается запрос на продолжение):
 
      $ ! Вносит программную единицу
 
      $ ! Два параметра - P1 = имя файла программной единицы
 
      $ !                 P2 = имя пользователя, вносящего файл
 
      $ !
 
      $ ON ERROR THEN GOTO EXIT
 
      $ FOUND = "FALSE"
 
      $ !
 
      $ ! Открыть CHECKOUT.DAT на чтение и его новую копию на


                                - 208 -
 
                          00152-01 97 09-1
 
 
      $ ! запись
 
      $ OPEN OLDOUT $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ OPEN/WRITE NEWOUT $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ !
 
      $ ! Читать записи до конца файла
 
      $UPLOOP:
 
      $ READ/END_OF_FILE=ENDUPLOOP OLDOUT CHECKOUT_LINE
 
      $ ! Искать имя программной единицы
 
      $ MODULE_LEN = F$LOCATE (" ", CHECKOUT_LINE)
 
      $ MODULE = F$EXTRACT (0, MODULE_LEN, CHECKOUT_LINE)
 
      $ ! Если не она, переписать запись в новый файл
 
      $ IF MODULE .EQS. P1 THEN GOTO USERNAME
 
      $ WRITE NEWOUT CHECKOUT_LINE
 
      $ GOTO UPLOOP
 
      $ ! Иначе имя программной единицы найдено
 
      $USERNAME:
 
      $ FOUND = "TRUE"
 
      $ ! Искать имя пользователя
 
      $ NAME = CHECKOUT_LINE - (MODULE + " ")
 
      $ NAME_LEN = F$LOCATE (" ", NAME)
 
      $ NAME = F$EXTRACT (O, NAME_LEN, NAME)
 
      $ ! Если имя программной единицы и пользователя верны, то
 
      $ ! переписать исходный файл, убрав его старые копии, и
 
      $ ! убрать запись из CHECKOUT.DAT отказом от ее переписи
 
      $ IF NAME .NES. P2 THEN GOTO ANOTHER
 
      $ COPY 'P1' .FOR $DISK1:[DEV.INCOME] 'P1'.FOR
 
      $ PURGE $DISK1:[DEV.INCOME] 'P1'.FOR
 
      $ GOTO UPLOOP
 
      $ ! Иначе дать сообщение об ошибке и переписать запись
 
      $ANоTHER:


                                - 209 -
 
                          00152-01 97 09-1
 
 
      $ WRITE SYS$OUTPUT "программная единица вынесена другими"
 
      $ WRITE SYS$OUTPUT CHECKOUT_LINE
 
      $ WRITE NEWOUT CHECKOUT_LINE
 
      $ GOTO UPLOOP
 
      $ENDUPLOOP:
 
      $ ! Изменить состояние из EOF на успешное
 
      $ $STATUS = 1
 
      $ ! Спросить о копировании, если программная единица не
 
      $ ! найдена
 
      $ IF FOUND THEN GOTO EXIT
 
      $ INQUIRE YN "программа не вынесена. Копировать (Y или N)?"
 
      $ IF YN THEN COPY 'P1'.FOR $DISK1:[DEV.INCOME]'P1'.FOR
 
      $ !
 
      $ ! Закрыть файлы регистрации выноса, убрать старые копии
 
      $ ! и выйти
 
      $EXIT:
 
      $ STATUS = $STATUS
 
      $ CLOSE OLDOUT
 
      $ CLOSE NEWOUT
 
      $ PURGE $DISK1:[DEV.INCOME]CHECKOUT.DAT
 
      $ EXIT (STATUS)
 
           Командные процедуры CHECKIN.COM и CHECKOUT.COM  эффек-
 
      тивны, если их применяют все,  работающие  над  программой.
 
      Никто не должнен  иметь  права  сокращать  этот  процесс  с
 
      помощью непосредственной команды COPY.
 
           Если не все, работающие над программой, имеют  один  и
 
      тот же код идентификации пользователя (UIC), то  необходимо
 
      обеспечить для всех доступ на чтение и  запись  к  каталогу
 
      построения. Вот несколько способов для этого:
 
           1) сделать всех программистов членами одной  группы  и


                                - 210 -
 
                          00152-01 97 09-1
 
 
      обеспечить членам группы полный доступ к  библиотеке  пост-
 
      роения;
 
           2) использовать список управления доступом для обеспе-
 
      чения всем программистам полного доступа к библиотеке пост-
 
      роения;
 
           3) установить специальный учетный  раздел,  в  котором
 
      должны регистрироваться  программисты,  чтобы  пользоваться
 
      процедурами вынесения и внесения;
 
           4) снабдить программистов привилегией SYSPRV. В проце-
 
      дуре вынесения или копирования исходного  файла  в  каталог
 
      построения  использовать  BACKUP  вместо  COPY  и  задавать
 
      /OWNER=PARENT .
 
 
 
           4.2. Идентификаторы
 
 
           Идентификаторы - это имена, обеспечивающие  расположе-
 
      ния (адреса) в виртуальной памяти. Значение  идентификатора
 
      есть адрес первого из младших байтов определенной области в
 
      виртуальной памяти, в  то  время  как  характеристики  этой
 
      области задают число байтов, на  которые  можно  ссылаться.
 
      Например, если TOTAL_HOUSES определено как целое, то  иден-
 
      тификатору TOTAL_HOUSES будет присвоен адрес младшего байта
 
      четырехбайтной области в  виртуальной  памяти.  СП  ФОРТРАН
 
      требует определения всех переменных и кодов через идентифи-
 
      каторы и дает возможность назначения идентификаторов  конс-
 
      тантам. СП ФОРТРАН производит  все  назначения  виртуальных
 
      адресов, сам программист не может назначать истинные  адре-
 
      са, но может назначать непрерывные области памяти для  раз-
 
      мещения переменных в массивах,  записях  или  блоках  общей
 
      памяти. Некоторые компоненты системы  (например,  отладчик)
 


                                - 211 -
 
                          00152-01 97 09-1
 
 
      позволяют ссылаться на области виртуальной памяти по истин-
 
      ным адресам, но рекомендуется ссылка по идентификаторам.
 
 
 
           4.2.1. Определение идентификаторов
 
 
           Идентификатор может содержать до 31 буквы, цифры, зна-
 
      ка подчеркивания или знака денежной единицы. Буквы верхнего
 
      и нижнего регистров  равноправны.  По  договоренности  знак
 
      денежной единицы ($)  зарезервирован  для  идентификаторов,
 
      применяемых в системных компонентах. При использовании зна-
 
      ка '$' в произвольных идентификаторах могут быть  случайные
 
      совпадения этих идентификаторов с системно-определенными. В
 
      программах на  языке  ФОРТРАН  идентификаторы  определяются
 
      следующими способами:
 
           1) переменные. Явное или неявное  описание  переменных
 
      отводит область виртуальной памяти под значение  переменной
 
      и связывает эту область с идентификатором;
 
           2) блоки переменных. Описание блока общей памяти поме-
 
      щает переменные в одну физическую область виртуальной памя-
 
      ти и связывает начало этой области с идентификатором;
 
           3) константы. Оператор PARAMETER  присваивает  неизме-
 
      няемое значение области виртуальной памяти и связывает  эту
 
      область с идентификатором;
 
           4) код. Операторы  PROGRAM,  SUBROUTINE  или  FUNCTION
 
      начинают блок кодов (программы или подпрограммы)  и  связы-
 
      вают идентификатор с первым  выполняемым  оператором  этого
 
      блока (идентификатор -  имя  программы  или  подпрограммы).
 
      Если программа не начинается оператором PROGRAM, то  именем
 
      программы будет имя файла, содержащего программу.  Оператор
 
      ENTRY также связывает идентификатор с выполняемым  операто-
 


                                - 212 -
 
                          00152-01 97 09-1
 
 
      ром.
 
 
 
           4.2.2. Локальные и глобальные идентификаторы
 
 
           Идентификаторы  по  своей  области   действия   бывают
 
      локальные и глобальные.
 
           На локальный идентификатор можно  ссылаться  только  в
 
      той  программной  единице,  в  которой  он  определен.  Имя
 
      локального идентификатора не может повторяться среди других
 
      имен идентификаторов в той же программной единице, но может
 
      быть повторено в другой программной единице той же програм-
 
      мы. Ссылки на локальные идентификаторы разрешаются во время
 
      трансляции. В языке ФОРТРАН имена всех переменных  и  конс-
 
      танты вида PARAMETER являются локальными идентификаторами;
 
           На глобальный идентификатор можно ссылаться извне  той
 
      программы, в которой он определен. Глобальный идентификатор
 
      должен иметь имя, отличающееся от  имен  других  глобальных
 
      идентификаторов во всей  программе.  Ссылки  на  глобальные
 
      идентификаторы разрешаются во  время  компоновки.  В  языке
 
      фортран глобальными идентификаторами являются  имена  прог-
 
      рамм, подпрограмм, входных точек и блоков общей памяти.
 
           Ссылки на глобальные идентификаторы в выполняемой час-
 
      ти программы - это обычно вызовы  подпрограмм.  Если  нужно
 
      сослаться на идентификатор в другом качестве - как на пара-
 
      метр или значение данных, то идентификатор следует  опреде-
 
      лить как EXTERNAL или INTRINSIC (оператор  INTRINSIC  упот-
 
      ребляется для встроенных функций и подпрограмм сп  фортран)
 
      в описательной части программной единицы.
 
           Системные средства, такие, как обслуживающая программа
 
      MESSAGE и макроассемблер, используют глобальные идентифика-
 


                                - 213 -
 
                          00152-01 97 09-1
 
 
      торы для определения значений данных. Язык  ФОРТРАН  разре-
 
      шает ссылаться на глобальные значения данных,  определенных
 
      в других программах,  но  не  дает  возможности  определять
 
      такие    глобальные    идентификаторы     (только     через
 
      COMMON-блоки).
 
 
 
           4.2.3. Ссылка на глобальные идентификаторы
 
 
           Для ссылки на глобальные идентификаторы в языке ФОР -
 
      РАН необходимо определить идентификатор внешним  (EXTERNAL)
 
      и ссылаться на него  как  на  параметр  встроенной  функции
 
      %LOG. Например, фрагмент программы ссылается на  глобальный
 
      идентификатор   RMS$_EOF   (код    условия,    возвращенный
 
      LIB$GET_INPUT):
 
      CHARACтER*255   NEW_TEXT
 
      INTEGER         STATUS
 
      INTEGER*2       NT_SIZ
 
      INTEGER         LIB$GET_INPUT
 
      EXTERNAL        RMS$_EOF
 
      STATUS = LIB$GET_INPUT (NEW_TEXT,
 
      2                       'NEW TEXT:',
 
      2                       NT_SIZ)
 
      IF ((.NOT. STATUS) .AND.
 
      2   (STATUS .NE. %LOG (RMS$_EOF))) THEN
 
        CALL LIB$SIGNAL (RETURN_STATUS BY VALUE)
 
      END IF
 
           Коды условий и другие идентификаторы,  определенные  в
 
      библиотеках системных об'ектных модулей и разделяемых обра-
 
      зов определены также в специальной  библиотеке  СП  ФОРТРАН
 
      FORSYSDEF.TLB. Использование определений  из  FORSYSDEF.TLB
 


                                - 214 -
 
                          00152-01 97 09-1
 
 
      позволяет ссылаться на  коды  условий  и  другие  системно-
 
      определенные идентификаторы как на локальные, а не глобаль-
 
      ные идентификаторы.
 
 
 
           4.2.4. Разрешение глобальных идентификаторов
 
 
           Ссылки на глобальные идентификаторы разрешаются  вклю-
 
      чением  при  компоновке  модуля,  определяющего  глобальный
 
      идентификатор. Когда компоновщик находит глобальный иденти-
 
      фикатор, он использует следующий алгоритм для поиска  опре-
 
      деляющего модуля:
 
           1) явно названные модули и библиотеки используются для
 
      разрешения глобальных идентификаторов, определенных пользо-
 
      вателем, таких, как подпрограммы и коды условий. Эти модули
 
      и библиотеки просматриваются в том порядке, в  котором  они
 
      заданы;
 
           2) системные библиотеки по умолчанию используются  для
 
      разрешения системно-определенных глобальных идентификатров,
 
      таких, как имена процедур и коды условий;
 
           3) библиотеки пользователя по  умолчанию  используются
 
      для избежания явного указания библиотек, тем самым  упрощая
 
      компоновку.
 
           Если компоновщик не может найти идентификатор,  то  он
 
      об'является неразрешенным (неопределенным) и выдается  пре-
 
      дупреждение. Можно прогонять образ, содержащий  неразрешен-
 
      ные идентификаторы; образ успешно выполняется до  тех  пор,
 
      пока не обратится к неразрешенному  идентификатору.  Напри-
 
      мер, если программа вызывает неопределенную подпрограмму, а
 
      подпрограмма не выполняется, то образ выполняется успешно.
 
           Если образ обращается к неразрешенному идентификатору,
 


                                - 215 -
 
                          00152-01 97 09-1
 
 
      то результат непредсказуем. Обычно образ прекращает  работу
 
      из-за нарушения доступа к памяти (попытка доступа  к  физи-
 
      ческой памяти, находящейся за пределами адресов  модулей  в
 
      библиотеке).
 
           Разрешить глобальный  идентификатор  можно,  указав  в
 
      команде компоновщика имя определяющего  этот  идентификатор
 
      модуля. Например, если программная единица INCOME ссылается
 
      на подпрограмму GET_STATS,  то  разрешить  ссылку  на  этот
 
      идентификатор можно при  компоновке  INCOME,  указав  файл,
 
      содержащий об'ектный модуль GET_STATS:
 
      $ LINK INCOME, GETSTATS
 
           Если модули, определяющие  глобальные  идентификаторы,
 
      находятся в об'ектной библиотеке, то нужно указать имя этой
 
      библиотеки в командной строке компоновщика. Например,  если
 
      GET_STATS  находится   в   библиотеке   об'ектных   модулей
 
      INCOME.OLB, нужно издать команду:
 
      $ LINK INCOME,INCOME/LIBRARY
 
 
 
           4.2.4.1. Системные библиотеки по умолчанию
 
 
           При  операциях  компоновки  автоматически   проводится
 
      поиск в системных библиотеках об'ектных модулей и разделяе-
 
      мых образов любых ссылок на глобальные  идентификаторы,  не
 
      разрешенные в явно заданных модулях и библиотеках.  Систем-
 
      ные библиотеки  об'ектных  модулей  и  разделяемых  образов
 
      включают входные точки для процедур из библиотеки  исполни-
 
      тельных процедур и системных служебных программ, коды усло-
 
      вий и другие  системно-определенные  значения.  Вызов  этих
 
      модулей не требует никаких действий во время компоновки.
 


                                - 216 -
 
                          00152-01 97 09-1
 
 
           4.2.4.2. Библиотеки пользователя по умолчанию
 
 
           Создаваемую  новую  процедуру  общего  назначения  или
 
      определяемый идентификатор общего назначения  можно  помес-
 
      тить в библиотеку пользователя по  умолчанию.  Тогда  можно
 
      компоновать модули, содержащие эти процедуры и  идентифика-
 
      торы, с любыми другими модулями, не называя библиотеку явно
 
      в команде LINK. Чтобы  определить  библиотеку  пользователя
 
      библиотекой по умолчанию, нужно приравнять имя ее  файла  к
 
      логическому имени LNK$LIBRARY. Например, библиотека в файле
 
      PROCEDURES.OLB (тип файла по умолчанию OLB) из $DISK1:[DEV]
 
      определяется как библиотека по умолчанию:
 
      $ DEFINE LNK$LIBRARY $DISK1:[DEV]PROCEDURES
 
           Дополнительные библиотеки  пользователя  по  умолчанию
 
      можно определить как  LNK$LIBRARY_1,  LNK$LIBRARY_2  и  так
 
      далее до LNK$LIBRARY_999.  Пропуск  номеров  библиотеки  не
 
      допускается.
 
           Чтобы сделать библиотеку доступной для любого  пользо-
 
      вателя системы, нужно определить ее  на  системном  уровне.
 
      Для ограничения  использования  библиотеки  или  подавления
 
      действия системной библиотеки нужно  определить  библиотеку
 
      на уровне процесса или группы. Например, библиотека пользо-
 
      вателя по умолчанию определяется на системном уровне:
 
      $ DEFINE/SYSTEM LNK$LIBRARY $DISK1:[DEV]PROCEDURES
 
           Когда компоновщик разрешает ссылки на глобальные иден-
 
      тификаторы, он первым  делом  просматривает  библиотеки  по
 
      умолчанию на уровне процесса, затем  библиотеки  на  уровне
 
      группы и системы.  Внутри  уровня  сначала  просматривается
 
      библиотека,    определенная    как    LNK$LIBRARY,    затем
 
      LNK$LIBRARY_1, LNK$LIBRARY_2 и т.д.
 


                                - 217 -
 
                          00152-01 97 09-1
 
 
           4.2.4.3. Библиотеки макроопределений
 
 
           Некоторые системные  идентификаторы  не  определены  в
 
      системных библиотеках об'ектных модулей и разделяемых обра-
 
      зов. В таких случаях эти идентификаторы определены  в  сис-
 
      темной библиотеке макроопределений и сообщается имя  MACRO,
 
      содержащего эти идентификаторы. Для доступа к таким иденти-
 
      фикаторам следует вначале оттранслировать программу на мак-
 
      роассеблере со следующим  исходным  кодом  (ключевое  слово
 
      GLOBAL  должно  быть  набрано  буквами  верхнего  регистра.
 
      Директива .TITLE подобна оператору PROGRAM  языка  ФОРТРАН,
 
      она не обязательна, но рекомендуется):
 
       .TITLE имя_макро
 
       имя_макро   GLOBAL
 
      !
 
      ! Определения
 
      !
 
       .END
 
           Пример.
 
      LBRDEF.MAR - файл с определениями системных MACRO:
 
       .TITLE $LBRDEF
 
       $LBRDEF GLOBAL
 
       $LHIDEF GLOBAL
 
       .END
 
           Программу,  содержащую  необходимые  макроопределения,
 
      нужно транслировать с помощью команды MACRO. Результирующий
 
      об'ектный модуль можно поместить в библиотеку по  умолчанию
 
      или в библиотеку, указываемую в команде LINK,  или  указать
 
      модуль в команде LINK. Например, перед операцией компоновки
 
      модули $LBRDEF и $LHIDEF помещаются в библиотеку:
 


                                - 218 -
 
                          00152-01 97 09-1
 
 
      $ MACRO LBRDEF
 
      $ LIBRARY/REPLACE INCOME LBRDEF
 
      $ DELETE LBRDEF.OBJ;*
 
      $ LINK INCOME,INCOME/LIBRARY
 
      Об'ектный модуль  непосредственно  используется  в  команде
 
      LINK:
 
      $ LINK INCOME,LBRDEF,INCOME/LIBRARY
 
 
 
           4.2.5. Библиотеки и файлы определений СП ФОРТРАН
 
 
           В  библиотеке   системных   определений   СП   ФОРТРАН
 
      SYS$LIBRARY:FORSYSDEF.TLB инициализируются коды  условий  и
 
      другие  значения,  определенные  в  системных   библиотеках
 
      об'ектных модулей и  разделяемых  образов  (за  исключением
 
      значений, специфичных для БЕЙСИКа или КОБОЛа), с  использо-
 
      ванием операторов определения данных - в основном  операто-
 
      ров PARAMETER. Вместо доступа к идентификатору как глобаль-
 
      ному можно включить в программу определение  идентификатора
 
      FORSYSDEF, указав имя содержащего идентификатор  библиотеч-
 
      ного модуля в операторе INCLUDE.
 
           Например, код  условия  RMS$_EOF  определен  в  модуле
 
      $RMSDEF библиотеки системных определений СП ФОРТРАН и  биб-
 
      лиотеки системных об'ектных и  разделяемых  образов.  Чтобы
 
      использовать этот идентификатор в программной единице, нуж-
 
      но задать оператор  INCLUDE  в  описательной  части  модуля
 
      (спецификатор INCLUDE должен быть задан как литерал):
 
      INCLUDE '($LIBDEF)'
 
           Оператор INCLUDE включает операторы определения данных
 
      в программу так, что имена значений  становятся  локальными
 
      идентификаторами. Для ссылки  на  идентификатор  достаточно
 


                                - 219 -
 
                          00152-01 97 09-1
 
 
      указать его имя; %LOC применять не надо. Определения значе-
 
      ний должны быть включены в каждую программную единицу,  где
 
      они используются, а не только, например, в  головную  прог-
 
      рамму.
 
           Если идентификатор определен только в системной макро-
 
      библиотеке, то для ссылки на идентификатор нужно  использо-
 
      вать программу MACRO.
 
           Можно создавать свои собственные "глобальные" значения
 
      тем же способом, каким это делает FORSYSDEF.TLB  -  помещая
 
      операторы PARAMETER, блоки общей памяти и другие  операторы
 
      определения данных в текстовые файлы, которые  будут  вклю-
 
      чаться в программные единицы, написанные на языке  фортран.
 
      Если есть много модулей для включения, то их следует помес-
 
      тить в текстовую библиотеку.  Например,  операторы  задания
 
      параметра переписываются из SCREEN.TXT в существующий текс-
 
      товый библиотечный файл с именем SYMBOLS.TLB (тип файла TLB
 
      по умолчанию), а исходный текстовый файл удаляется:
 
      $ LIBRARY/TEXT/REPLаCE SYMBOLS SCREEN
 
      $ DELETE SCREEN.TXT;*
 
           Для включения модуля из текстовой библиотеки необходи-
 
      мо указать имя модуля в скобках после имени библиотеки (имя
 
      текстовой    библиотеки    по     умолчанию     принимается
 
      SYS$SYSTEM:FORSYSDEF):
 
      INCLUDE $DISK1:[FORDEF]SYMBOLS(SCREEN)'
 


                                - 220 -
 
                          00152-01 97 09-1
 
 
           4.3. Разделяемые образы
 
 
           Разделяемый образ есть  невыполняемый  образ,  который
 
      может быть скомпонован в составе выполняемого образа.  Если
 
      есть программная единица, вызываемая более чем одной  прог-
 
      раммой, то компоновка ее в виде  разделяемого  образа  дает
 
      следующие преимущества:
 
           1) экономит пространство на диске. Выполняемый  образ,
 
      с которым скомпонован разделяемый образ, не  включает  этот
 
      разделяемый образ фактически. Существует только одна  копия
 
      разделяемого образа;
 
           2) упрощает работу. Если используются вектора передачи
 
      и возможность GSMATCH, то можно модифицировать,  перетранс-
 
      лировать и перекомпоновывать разделяемый образ без  необхо-
 
      димости перекомпоновывать какой-либо выполняемый  образ,  с
 
      ним скомпонованный.
 
           Разделяемые образы могут также экономить память,  если
 
      они установлены как разделяемые.
 
 
 
           4.3.1. Создание разделяемых образов
 
 
           Для создания разделяемых  образов  необходимо  сделать
 
      следующее:
 
           1) написать  и  оттранслировать  программную  единицу,
 
      которая будет разделяться различными программами. Если  эта
 
      программная единица вызывает другие  подпрограммы,  то  они
 
      также должны быть включены в разделяемый образ;
 
           2) написать вектора передачи  для  каждой  программной
 
      единицы  из  разделяемого  образа  на  языке  MACRO.  Может
 
      использоваться следующий  шаблон,  содержащий  один  вектор
 
      передачи; три средних  оператора  повторяются  для  каждого


                                - 221 -
 
                          00152-01 97 09-1
 
 
      дополнительного вектора передачи:
 
       .TITLE VEKTOR_NAME     !Имя вектора
 
       .TRANSFER ROUTINE_NAME !Имя подпрограммы
 
       .MASK ROUTINE_NAME
 
       JMP L^ROUTINE_NAME+2
 
       .END
 
           Программы на языках MACRO и ФОРТРАН  не  должны  иметь
 
      одинаковые имена, так как нежелательно, чтобы после  транс-
 
      ляции появились два об'ектных модуля с одинаковыми именами.
 
      Следующий файл повтора передачи предназначен для  программ-
 
      ной единицы GET_1_STAT:
 
       .TITLE X_GET_1_STAT
 
       .TRANSFER GET_1_STAT
 
       .MASK GET_1_STAT
 
       JMP L^GET_1_STAT+2
 
       .END
 
           Трансляция вектора  передачи  командой  MACRO  создает
 
      об'ектный модуль X_GET_1_STAT в файле XGET1STAT.OBJ:
 
      $ MACRO XGET1STAT
 
           3) написать файл возможностей. Для  помещения  вектора
 
      передачи в начало разделяемого образа  применяется  возмож-
 
      ность CLUSTER. Возможность GSMATCH используется  для  того,
 
      чтобы определить, может ли выполняемый образ,  скомпонован-
 
      ный с данным разделяемым образом, иметь доступ к  модифици-
 
      рованной версии разделяемого образа без перекомпоновки.
 
           Возможность CLUSTER имеет форму:
 
 
 
      CLUSTER=CLUSTER_NAME,,,FILENAME
 
 
 
      где CLUSTER_NAME - имя кластера по выбору пользователя;


                                - 222 -
 
                          00152-01 97 09-1
 
 
          FILENAME     - имя об'ектного файла, содержащего
 
                         вектор передачи.
 
           Возможность GSMATCH имеет форму:
 
 
 
      GSMATCH=KEYWORD,MAJOR_ID, MINOR_ID
 
 
 
      где KEYWORD  - ключевое слово;
 
          MAJOR_ID - старшая (предыдущая) версия;
 
          MINOR_ID - младшая (обновленная) версия.
 
           Обычно при создании разделяемого  образа  используется
 
      ключевое слово LEQUAL и задаются какие-нибудь целые  значе-
 
      ния для старшей и младшей версий; при обновлении  разделяе-
 
      мого образа задается ключевое слово LEQUAL с той же старшей
 
      версией и увеличенным на единицу  номером  младшей  версии.
 
      Такое употребление слова LEQUAL дают возможность  выполняе-
 
      мому образу получить доступ  к  новой  версии  разделяемого
 
      образа без перекомпоновки, но препятствует доступу к старой
 
      версии разделяемого образа.
 
           Пример 1.
 
           При создании разделяемого образа GET1STAT можно задать
 
      следующие возможности (файл GET1STAT.OPT):
 
      CLUSTER=X_GET_1_STAT,,,XGET1STAT
 
      GSMATCH=LEQUAL,1,100
 
           Пример 2.
 
           При обновлении разделяемого образа возможность GSMATCH
 
      меняется:
 
      CLUSTER=X_GET_1_STAT,,,XGET1STAT
 
      GSMATCH=LEQUAL,1,101
 
           4) компоновка с целью получения  разделяемого  образа.
 
      Для создания разделяемого образа применяется команда LINK с


                                - 223 -
 
                          00152-01 97 09-1
 
 
      квалификатором /SHAREABLE и заданием компоновщику в качест-
 
      ве входных параметров об'ектного модуля (модулей)  и  файла
 
      возможностей. Например, команда  LINK  создает  разделяемый
 
      образ  с   именем   GET1STAT.EXE   из   об'ектного   модуля
 
      CET1STAT.OBJ и файла возможностей GET1STAT.OPT:
 
      $ LINK/SHAREABLE GET1STAT,GET1STAT/OPTION ,
 
      файл GET1STAT.OPT содержит:
 
      CLUSTER=X_GET_1_STAT,,,XGET1STAT
 
      GSMATCH=LEQUAL,1,100
 
      После  создания  разделяемого   образа   об'ектные   модули
 
      GET1STAT.OBJ и XGET1STAT.OBJ можно удалить.
 
           Если компонуется разделяемый образ, то ссылки на  гло-
 
      бальные идентификаторы должны быть разрешены  включением  в
 
      операцию компоновки модулей, определяющих эти идентификато-
 
      ры. Например, для создания разделяемого образа из программ-
 
      ной единицы GET_STATS, ссылающейся на  программную  единицу
 
      GET_1_STAT, следует задать как GETSTATS.OBJ (файл, содержа-
 
      щий  GET_STATS),  так  и  GET1STAT.OBJ  (файл,   содержащий
 
      GET_1_STAT) в качестве входных файлов компоновщика:
 
      $ LINK/SнAREABLE GETSTATS,GET1STAT,GETSTATS/OPTION
 
      Файл GETSTATS.OPT содержит:
 
      CLUSTER=X_GET_STATS,,,XGETSTATS
 
      GSMATCH=LEQUAL,1,100
 
           В любой большой разработке следует держать программные
 
      единицы в библиотеках (либо об'ектные модули,  либо  разде-
 
      ляемые образы) для упрощения работы с ними и процесса  ком-
 
      поновки. Библиотеки разделяемых образов,  называемые  также
 
      библиотеками таблиц  идентификаторов  разделяемых  образов,
 
      отличаются от библиотек об'ектных модулей тем, что  в  биб-
 
      лиотеку разделяемых образов помещается не  сам  разделяемый


                                - 224 -
 
                          00152-01 97 09-1
 
 
      образ, а его таблица идентификаторов; поэтому удалять  раз-
 
      деляемый образ после помещения его в библитотеку недопусти-
 
      мо.
 
           Например, команды LINK и LIBRARY  создают  разделяемые
 
      образы GET1STAT.EXE и GETSTATS.EXE, помещая их в библиотеку
 
      разделяемых образов  INCOMESHR.  INCOMESHR  упоминается  во
 
      второй  команде  компоновки  для   разрешения   ссылки   на
 
      GET_1_STAT из GET_STATS:
 
      $ LINK/SHAREABLE GET1STAT,GET1STAT/OPTION
 
      $ LIBRARY/SHAREABLE/REPLACE INCOMESHR GET1STAT
 
      $ LINK/SHAREABLE GETSTATS,GETSTATS/OPTION,INCOMESHR/LIBRARY
 
      $ LIBRARY/SHAREABLE/REPLACE INCOMESHR GETSTATS
 
           Если сделать попытку создать GETSTATS.EXE раньше,  чем
 
      GET1STAT.EXE, компоновщик не  сможет  разрешить  ссылку  на
 
      GET_1_STAT и выдаст предупреждение о неопределенном символе
 
      GET_1_STAT:
 
      %LINK-W-USEUNDEF, 1 UNDEFINED SYMBOL:
 
      %LINK-I-UDFSYM,    GET_1_STAT
 
 
 
           4.3.1.1. Векторы передачи
 
 
           Вектор  передачи  размещается  в  начале  разделяемого
 
      образа для указания на программную единицу в этом разделяе-
 
      мом образе. Обычно разделяемый образ  содержит  одну  прог-
 
      раммную единицу и один вектор передачи. Если в  разделяемом
 
      образе более  одной  ппрограммной  единицы,  то  необходимо
 
      включить вектор передачи для каждой из них.
 
           Пример.
 
           Программная единица на языке MACRO содержит два векто-
 
      ра передачи - один для GET_1_STAT и один для GET_STATS:
 


                                - 225 -
 
                          00152-01 97 09-1
 
 
       .TITLE X_GET_STATS
 
       .TRANSFER GET_1_STAT
 
       .MASK GET_1_STAT
 
       JMP L^GET_1_STAT+2
 
       .TRANSFER GET_STATS
 
       .MASK GET_STATS
 
       JMP L^GET_STATS+2
 
       .END
 
           Векторы передачи следует использовать всегда; они поз-
 
      воляют модифицировать разделяемый образ без  перекомпоновки
 
      любого скомпонованного с ними  выполняемого  образа.  Когда
 
      разделяемый образ  подвергается  компоновке  для  получения
 
      исполняемого образа, компоновщик разрешает ссылку на  прог-
 
      раммную единицу из  разделяемого  образа,  используя  адрес
 
      вектора передачи для данной программной единицы.
 
           Если модифицируется программная единица в  разделяемом
 
      образе, то стартовый адрес одной или нескольких программных
 
      единиц этого образа может измениться; перекомпоновка разде-
 
      ляемого образа исправляет вектора передачи  таким  образом,
 
      что они показывают на начало связанных с  ними  программных
 
      единиц. Так как адреса векторов передачи  не  меняются,  то
 
      выполняемый образ, скомпонованный с разделяемым, перекомпо-
 
      новки не требует.
 
           Не следует удалять  вектор  передачи  из  разделяемого
 
      образа, содержащего более одного вектора передачи. Удаление
 
      одного вектора передачи может изменить адреса других векто-
 
      ров передачи в разделяемом образе. Если адрес вектора пере-
 
      дачи изменился, то необходимо перекомпоновать все выполняе-
 
      мые образы, ссылающиеся на  этот  разделяемый  образ.  Если
 
      приходится удалять программу из разделяемого образа, содер-


                                - 226 -
 
                          00152-01 97 09-1
 
 
      жащего более одной программной единицы, нужно создать  пус-
 
      тую программную единицу с тем же именем, например:
 
      FUNCTION GET_1_STAT (ROW,
 
      2                    COLUMN,
 
      2                    STAT)
 
      ! Пустая программа
 
      END
 
      Оттранслировать пустую программную единицу  и  перекомпоно-
 
      вать разделяемый образ. В новой версии разделяемого  образа
 
      вектор передачи для "удаленной" программной  единицы  будет
 
      указывать на пустую программную единицу.
 
 
 
           4.3.1.2. Возможность GSMATCH
 
 
           Возможность GSMATCH позволяет указать, имеет ли выпол-
 
      няемый образ, скомпонованный с разделяемым, доступ к  моди-
 
      фицированному  разделяемому  образу.  Возможность   GSMATCH
 
      должна  быть  задана  в  файле  возможностей  (квалификатор
 
      /OPTION в команде LINK).
 
           Когда на  этапе  прогона  выполняемый  образ  пытается
 
      получить доступ к разделяемому  образу,  система  проверяет
 
      наличие возможности GSMATCH у разделяемого образа,  который
 
      был изначально скомпонован с выполняемым образом. С возмож-
 
      ностью GSMATCH могут быть заданы следующие ключевые слова:
 
           LEQUAL - если младшая версия (более новая) изначально-
 
      го разделяемого образа меньше  либо  равнA  младшей  версии
 
      разделяемого образа, к которому  пытается  получить  доступ
 
      выполняемый образ, система  разрешает  выполняемому  образу
 
      доступ к разделяемому;
 
           EQUAL - если младшая версия изначального  разделяемого
 


                                - 227 -
 
                          00152-01 97 09-1
 
 
      образа равна младшей версии разделяемого образа, к которому
 
      пытается получить доступ выполняемый образ, система  разре-
 
      шает  доступ  выполняемого  образа  к  разделяемому  образу
 
      (установлено по умолчанию, если возможность GSMATCH явно не
 
      задана);
 
           ALWAYS - система разрешает выполняемому образу  доступ
 
      к разделяемому образу независимо от  значения  старшей  или
 
      младшей версий.
 
           Для анализа значений старшей и младшей  версий  разде-
 
      ляемого образа используется команда LINK/MAP/FULL  (получе-
 
      ние листинга образа, содержащего возможность GSMATCH).
 
 
 
           4.3.1.3. Возможность UNIVERSAL
 
 
           Универсальный идентификатор есть глобальный  идентифи-
 
      катор на который можно ссылаться извне разделяемого образа,
 
      его содержащего. Вектор передачи не только  создает  указа-
 
      тель программной единицы, но и делает ее  имя  унверсальным
 
      идентификатором. Чтобы сделать универсальным идентификатор,
 
      не являющийся  именем  программы,  используется  возожность
 
      UNIVERSAL в  файле  возможностей  (квалификатор  /OPTION  в
 
      команде LINK).
 
           Ссылка на универсальный идентификатор  разрешается  во
 
      время компоновки как смещение от  начала  определяющей  его
 
      программы. Из этого следует, что при модификации программы,
 
      определяющей универсальный  идентификатор,  необходимо  эту
 
      программу перекомпоновать, чтобы  скорректировать  смещение
 
      универсального идентификатора. Универсальные  идентификато-
 
      ры, созданные векторами передачи, всегда находятся в начале
 
      определяющего  модуля.  Поэтому  перекомпоновка  необходима
 


                                - 228 -
 
                          00152-01 97 09-1
 
 
      только при наличии универсального идентификатора, созданно-
 
      го с использованием возможности UNIVERSAL.
 
 
 
           4.3.2. Библиотеки разделяемых образов
 
 
           Для создания библиотеки  разделяемых  образов  исполь-
 
      зуются  команды  LIBRARY  с   квалификаторами   /CREATE   и
 
      /SHAREABLE, за которыми следует имя библитотеки.  Например,
 
      команда создания библиотеки разделяемых образов в  файле  с
 
      именем INCOMESHR.OLB (OLB есть тип файла по  умолчанию  для
 
      библиотек разделяемых образов и для  об'ектных  библиотек),
 
      выглядит так:
 
      $ LIBRARY/CREATE/SHAREABLE INCOMESHR
 
           Для добавления модулей  в  библиотеку  или  их  замены
 
      используется команда LIBRARY с квалификаторами /SHAREABLE и
 
      /REPLACE, за которыми следует имя библиотеки (первый  пара-
 
      метр) и имя файла разделяемого  образа  (второй  параметр).
 
      Тип файла разделяемого образа по умолчанию есть EXE. Напри-
 
      мер, команда включения таблицы идентификаторов разделяемого
 
      образа GET1STAT.EXE в разделяемую библиотеку  INCOMESнR.OLB
 
      выглядит следующим образом:
 
      $ LIBRARY/SHAREABLE/REPLACE INCOMESHR GET1STAT
 
           Просмотр библиотеки разделяемых образов выполняется  с
 
      помощью квалификатора /LIST команды LIBRARY. Удалить разде-
 
      ляемый образ из  библиотеки  разделяемых  образов  можно  с
 
      помощью квалификатора /DELETE команды LIBRARY.
 


                                - 229 -
 
                          00152-01 97 09-1
 
 
           4.3.3. Компоновка разделяемых образов
 
 
           Для задания разделяемого образа входным файлом  компо-
 
      новщика нужно задать либо имя библиотеки разделяемых  обра-
 
      зов, содержащей таблицу идентификаторов нужного разделяемо-
 
      го образа (для обозначения библиотечного файла используется
 
      квалификатор /LIBRARY), либо файл возможностей,  содержащий
 
      имя файла разделяемого образа (для обозначения файла разде-
 
      ляемого образа в файле возможностей используется квалифика-
 
      тор /SHAREABLE). Разделяемый образ должен быть специфициро-
 
      ван в файле возможностей, поскольку квалификатор /SHAREABLE
 
      в команде LINK интерпретируется как командный  квалификатор
 
      для создания разделяемого образа.
 
           Например,   команда   компоновки   об'ектного   модуля
 
      INCOME.OBJ с библиотекой INCOME.OLB и разделяемыми образами
 
      GETSTATS.EXE и GET1STAT.EXE может быть такой:
 
      $ LINK INCOME,INCOME/OPTION,INCOME/LIBRARY
 
      Файл INCOME.OPT содержит:
 
      GETSTATS/SHAREABLE
 
      GET1STAT/SHAREABLE
 
      Следующая команда компонует  об'ектный  модуль  INCOME.OBJ,
 
      библиотеку об'ектных модулей INCOME.OBJ и библиотеку разде-
 
      ляемых образов  INCOMESHR.OLB  для  получения  выполняемого
 
      образа в файле INCOME.EXE:
 
      $ LINK INCOME,INCOME/LIBRARY,INCOMESHR/LIBRARY
 
           На этапе компоновки  предполагается,  что  разделяемый
 
      образ находится в SYS$SHARE и имеет тип файла EXE. Поэтому,
 
      если разделяемый образ  не  находится  в  SYS$SHARE,  нужно
 
      определить логическое имя, приравнивающее имя  разделяемого
 
      образа к его полной спецификации файла.
 


                                - 230 -
 
                          00152-01 97 09-1
 
 
           Пример.
 
           Выполняемый образ INCOME.EXE ссылается на файлы разде-
 
      ляемых образов GETSTATS.EXE и GET1STAT.EXE. Если эти разде-
 
      ляемые образы находятся в SYS$SHARE,  то  выполнить  INCOME
 
      можно так:
 
      $ RUN INCOME
 
      Однако, если файлы разделяемых образов находятся  в  другом
 
      каталоге, то следует создать логические имена,  связывающие
 
      имена  этих  файлов  с  их   полной   спецификацией.   Если
 
      GETSTATS.EXE   и   GET1STAT.EXE   находятся   в    каталоге
 
      [INCOME.DEVELOP] на диске $DISK1, необходимо до  выполнения
 
      INCOME задать логические имена для файлов:
 
      $ DEFINE GETSTATS $DISK1:[INCOME.DEVELOP]GETSTATS
 
      $ DEFINE GET1STAT $DISK1:[INCOME.DEVELOP]GET1STAT
 
      $ RUN INCOME
 
           Попытка выполнить INCOME без задания  логических  имен
 
      приведет к выдаче сообщения  (по  умолчанию  имя  SYS$SHARE
 
      транслируется в SYS$SYSROOT:[SYSLIB]):
 
      %DCL-W-ACTIMAGE, ERROR ACTIVATING IMAGE GETSTATS
 
      -CLI-E-IMAGEFNF, IMAGE FILE NOT FOUND
 
                       SYS$SYSROOT:[SYSLIB]GETSTATS.EXE
 
      Вообще говоря, работая над программой, использующей  разде-
 
      ляемые образы, следует оставлять разделяемые образы в ката-
 
      логе разработки и определять логические имена  всякий  раз,
 
      когда начинается работа с программой. Если  придется  рабо-
 
      тать над  программой  несколько  сеансов,  можно  поместить
 
      логические имена в файл регистрации LOGIN.COM.  Как  только
 
      разделяемые образы будут отлажены, их можно  переместить  в
 
      SYS$SHARE и убрать логические имена из файла LOGIN.COM.
 


                                - 231 -
 
                          00152-01 97 09-1
 
 
           4.3.4. Установка разделяемых образов
 
 
           Чтобы дать возожность выполняемым образам пользоваться
 
      одной и той же копией разделяемого образа, последний  уста-
 
      навливается в памяти как разделяемый образ. Когда выполняе-
 
      мый образ, скомпонованный с разделяемым образом  обращается
 
      к нему, а копия этого образа уже находится в памяти, выпол-
 
      няемый образ пользуется именно этой копией. Если  же  копии
 
      разделяемого образа в  памяти  нет,  то  выполняемый  образ
 
      копирует его каждый раз в память. Если к разделяемому обра-
 
      зу не ожидается одновременного  доступа  более  чем  одного
 
      выполняемого образа, то можно не устанавливать его в памяти
 
      как разделенный образ.
 
           Установка разделяемого образа в памяти  подобна  уста-
 
      новке привилегированного образа,  но  вместо  квалификатора
 
      /PRIVILEGED используется квалификатор /SHARED.
 
 
 
           4.4. Получение листингов
 
 
           На этапах компиляции и компоновки можно получить  лис-
 
      тинги компиляции и карты образов.
 
 
 
           4.4.1. Листинги компиляции
 
 
           Для получения файла листинга используется квалификатор
 
      /LIST команды FORTRAN:
 
      $ FORTRAN/LIST INCOME
 
           Если программа транслируется в пакетном задании,  файл
 
      листинга генерируется по умолчанию; для подавления создания
 
      файла листинга следует задать квалификатор /NOLIST (в любом
 
      случае автоматической печати файла листинга нет). По  умол-
 


                                - 232 -
 
                          00152-01 97 09-1
 
 
      чанию файл листинга имеет имя первого из  исходных  файлов,
 
      указанных в команде FORTRAN и тип  файла  LIS.  Для  отмены
 
      этого умолчания можно ввести спецификацию файла с  квалифи-
 
      катором /LIST. Например, для получения  файла  листинга  со
 
      спецификацией INCOME1.LIS можно задать:
 
      $ FORTRAN/LIST=INCOME1.LIS INCOME
 
           Компоненты листинга зависят  от  параметров,  заданных
 
      вместе с квалификатором /LIST. По умолчанию листинг  содер-
 
      жит раздел исходной программы, карту памяти и характеристи-
 
      ки компиляции. Можно также затребовать  дополнительно  воз-
 
      можные исходные  строки,  машинные  коды  и  дополнительную
 
      информацию об идентификаторах.
 
 
 
           4.4.1.1. Листинг исходной программы
 
 
           Листинг исходной программы состоит из исходного текста
 
      с номерами строк, проставленных транслятором. Номера  строк
 
      расположены в левом поле по одному  на  каждую  строку;  на
 
      номера строк ссылаются сообщения об ошибках, а также  сооб-
 
      щения и команды отладчика. Если создается исходный текст  с
 
      номерами, порожденными  редактором,  то  эти  номера  будут
 
      находится слева от номеров строк, порожденных транслятором.
 
 
           Пример.
 
           Листинг функции CALC_SUMS с номерами  строк,  простав-
 
      ленными транслятором:
 
      0001    INTEGER FUNCTION CALC_SUMS (TOTAL_HOUSES,
 
      0002    2                           PERSONS_HOUSE,
 
      0003    2                           ADULTS_HOUSE,
 
      0004    2                           INCOME_HOUSE,
 
      0005    2                           AVG_PERSONS HOUSE,


                                - 233 -
 
                          00152-01 97 09-1
 
 
      0006    2                           AVG_ADULTS HOUSE,
 
      0007    2                           AVG_INCOME HOUSE,
 
      0008    2                           AVG_INCOME PERSON,
 
      0009    2                           MED_INCOME HOUSE)
 
      0010    !
 
              ! Продолжение
 
              ! описаний
 
              !
 
      0050    ! Определение среднего дохода на дом
 
      0051    J=1
 
      0052    DO I=2,101
 
      0053    IF (MEDIAN (I) .GT. MEDIAN (J)) J=I
 
      0054    END DO
 
      0055    MED_INCOME_HOUSE=J*1000
 
      0056
 
      0057    ! Возврат
 
      0058    CALC_SUMS=STATS_OK
 
      0059    END ! Конец подпрограммы  .
 
           Для включения в листинг строк текста,  которые  обычно
 
      опускаются, используется кроме квалификатора /LIST квалифи-
 
      катор /SHOW. Например,  чтобы  распечатать  иходные  строки
 
      файла,  заданного  в  операторе   INCLUDE,   нужно   задать
 
      /SHOW=INCLUDE; чтобы распечатать исходные строки,  сгенери-
 
      рованные  каким-нибудь  препроцессором,  необходимо  задать
 
      /SHOW=PREPROCESSOR.
 


                                - 234 -
 
                          00152-01 97 09-1
 
 
           4.4.1.2. Печать об'ектных кодов
 
 
           Для включения в листинг  сгенерированных  транслятором
 
      об'ектных кодов задается  квалификатор  /MACHINE_CODE  сов-
 
      местно с квалификатором /LIST:
 
      $ FORTRAN/LIST/MACHINE_CODE INCOME
 
           Первая  строка  заданного  кода   содержит   директиву
 
      .TITLE, показывающую имя  программной  единицы,  к  которой
 
      относятся машинные коды. Если программная единица  является
 
      головной, то заголовок будет либо именем, указанным в  опе-
 
      раторе PROGRAM, либо, если  оператор  PROGRAM  отсутствует,
 
      составным именем: "имя_файла$MAIN". Если программная едини-
 
      ца есть подпрограмма BLOCK DATA, то  заголовок  будет  либо
 
      именем, указанным в операторе BLOCK DATA, либо, если опера-
 
      тор   BLOCK   DATA   отсутствует   -   составным    именем:
 
      имя_файла$DATA (имя_файла - имя файла, содержащего исходный
 
      текст трансляции).
 
           Строки, следующие за  .TITLE,  содержат  информацию  о
 
      памяти, такую, как переменные, инициализированные в  опера-
 
      торах описания типов данных. За информацией о  памяти  сле-
 
      дуют машинные инструкции, записанные  с  помощью  мнемоники
 
      MACRO СМ 1700. Сгенерированные  транслятором  номера  печа-
 
      таются в правом поле, а виртуальные  адреса  начала  каждой
 
      строки пишутся в левом поле. Например, строка  53  исходной
 
      программной единицы CALC_SUMS транслируется  в  пять  строк
 
      машинного кода:
 
      00BB MOVL J(R11), R0                          ;0053
 
      00BF MOVL I(R11), R1
 
      00C3 CMPL MEDIAN-4(R11)[R1], MEDIAN-4(R11)[R0]
 
      00CC BLEQ L$6
 


                                - 235 -
 
                          00152-01 97 09-1
 
 
      00CE MOVL I(R11), J(R11)
 
           Общие регистры СМ 1700 (от  0  до  12)  представляются
 
      идентификаторами от R0 до R12. Когда регистр 12 использует-
 
      ся как указатель аргумента, он обозначается  AP.  Указатель
 
      кадра (регистр 13) обозначается FP, регистр 14 -  указатель
 
      стека - через SP, а счетчик инструкций (регистр 15) - через
 
      PC. Целые константы показываются как целые значения со зна-
 
      ком, действительные и комплексные константы - как  шестнад-
 
      цатиричные значения без знака, которым предшествует  символ
 
      ^X. Адреса представляются именем  программной  секции  плюс
 
      шестнадцатиричное смещение внутри этой секции.  Программные
 
      секции отмечаются строками  PSECT,  а  метки,  генерируемые
 
      транслятором для собственного употребления, имеют вид  L$N,
 
      где N-целое число.
 
 
           Пример.
 
           Часть листинга с об'ектным кодом  программной  единицы
 
      CALCSUMS:
 
              .TITLE CALC_SUMS
 
              .IDENT 01
 
      01AC    .PSECT $LOCAL
 
      01AC    .LONG ^X040A0004
 
      01BO    .LONG ^X00000000
 
      01B4    .LONG ^X01E00000, ^X00002000
 
      0000    .PSECT $CODE
 
      0000    CALC_SUMS::
 
      0000    .WORD ^M<IV,R11>
 
      0002    MOVAL $LOCAL+^X198, R11
 
      0009    MOVL PERSONS_HOUSE(AP), $LOCAL+^X1BO(R11)
 
      000E    SUBL3 #4, PERSONS_HOUSE(AP), $LOCAL+^X1BC(R11)
 


                                - 236 -
 
                          00152-01 97 09-1
 
 
      0014    MOVL ADULTS_нOUSE(AP), $LOCAL+^X1DO(R11)
 
      0019    SUBL3 #4, ADULTS_HOUSE(AP), $LOCAL+^X1DC(R11)
 
      001F    MOVL INCOME_HOUSE(AP), $LOCAL+^X1FO(R11)
 
      0024    SUBL3 #4, INCOME_HOUSE(AP), $LOCAL+^X1FC(R11)
 
                                                         ;0034
 
      002A    MOVL @TOTAL_HOUSES(AP), $LOCAL+^X20C(R11)
 
      002F    MOVL #1, I(R11)
 
      0033    CMPL I(R11), $LOCAL+^X20C(R11)
 
      0038    BGTR L$4
 
                                                         ; 0055
 
      00DC    MULL3 #1000, J(R11), @MED_INCOME_HOUSE(AP)
 
                                                         ;0058
 
      00E6    MOVL #1, CALC_SUMS(R11)
 
                                                         ;0059
 
      00EA    MOVL CALC_SUMS(R11), R0
 
      00EE    RET
 
              .END
 
 
 
           4.4.1.3. Карта памяти
 
 
           Карта памяти - это часть сгенерированного транслятором
 
      листинга, содержащая конспективную информацию о программных
 
      секциях, массивах и переменных. Можно запросить и  дополни-
 
      тельную информацию об идентификаторах в определенной секции
 
      карты памяти заданием квалификатора  /CROSS_REFERENCE  сов-
 
      местно с квалификатором /LIST.
 
           Необходимо заметить, что адреса данных заданы в  шест-
 
      надцатиричном виде как смещение относительно  начала  прог-
 
      раммной секции  или  указателя  аргумента  (AP).  Косвенная
 
      адресация указывается знаком @, за которым следует адресное
 


                                - 237 -
 
                          00152-01 97 09-1
 
 
      поле (при косвенной адресации адрес,  заданный  программной
 
      секцией (или AP) плюс смещение, указывает на адрес  данных,
 
      а не на сами данные).
 
           В карте памяти описываются следующие понятия:
 
           1) программные секции. Описывается каждая  программная
 
      секция, включая ее номер PSECT, имя, размер и  атрибуты,  и
 
      полная память, отведенная под все программные секции.  Нап-
 
      ример, программная секция в карте  памяти  описывается  как
 
      $CODE (PSECT, содержащая выполняемые операторы),  состоящая
 
      из 224 (десятичных)  байтов  кода,  позиционно-независимая,
 
      непрерывная, перемещаемая, локальная,  разделяемая,  выпол-
 
      няемая, читаемая, не допускающая в себя запись и  выравнена
 
      по границе длинного слова:
 
      PROGRAM SECTIONS
 
      NAME   BYTEN  ATTRIBUTES
 
      $CODE   224   PIC CON REL LCL   SHR   EXE RD NOWRT LONG
 
      $LOCAL  524   PIC CON REL LCL NOSHR NOEXE RD   WRT LONG
 
      TOTAL SPACE ALLOCATED 748
 
           2) входные  точки.  Перечисляются  все  входные  точки
 
      (выполняемые операторы, с которых может быть начато  выпол-
 
      нение программной единицы) и их  адреса.  Если  программная
 
      единица является функцией, то указывается также и  ее  тип.
 
      Например, программная единица CALC_SUMS есть  функция  типа
 
      INTEGER*4:
 
      ENTRY POINTS
 
      ADDRESS     TYPE  NAME
 
      0-00000000  I*4   CALC_SUMS
 
           3) сводка оператор-функций. Перечисляются входные точ-
 
      ки, типы и имена всех  оператор-функций.  Если  все  ссылки
 
      на оператор-функцию порождают линейный код, то тело  опера-


                                - 238 -
 
                          00152-01 97 09-1
 
 
      тор-функции не транслируется, а  вместо  адреса  появляется
 
      двойная звездочка (**);
 
           4) переменные. Перечисляются адреса, типы и имена всех
 
      переменных, кроме массивов. Например, если каждая  перемен-
 
      ная имеет тип REAL*4, будет напечатано:
 
      VARIABLES
 
         ADDRESS    TYPE   NAME
 
       2-0000019C   R*4   ADULTS
 
      AP-00000018@  R*4   AVG_ADULTS_HOUSE
 
      AP-0000001с@  R*4   AVG_INCOME_HOUSE
 
           5) массивы. Перечисляются адреса, типы  и  имена  всех
 
      массивов, для каждого указывается размер (в байтах) и число
 
      элементов (указанное как размерность).  Если  массив  имеет
 
      переменную или воспринимаемую длину, то его  размер  указы-
 
      вается двойной звездочкой (**), а каждая регулирующая  раз-
 
      мерность указывается как одинарная звездочка (*). Например,
 
      для одномерных массивов будет напечатано:
 
      ARRAYS
 
      ADDRESS         TYPE  NAME          BYTES  DIMENSIONS
 
      AP-0000000C@    R*4  ADULTS_HOUSE   8102   (2048)
 
      AP-00000010@    R*4  INCOME_HOUSE   8102   (2048)
 
       2-00000000     I*4  MEDIAN          404    (101)
 
      AP-00000008@    R*4  PERSUNS_HOUSE  8102   (2048)
 
           6) сводка списков имен. Перечисляются имя и адрес каж-
 
      дого списка имен;
 
           7) сводка меток. Перечисляются номера  и  адреса  всех
 
      определенных пользователем меток операторов. У меток опера-
 
      тора FORMAT в конце ставится апостроф ('). Если поле адреса
 
      метки содержит двойную звездочку (**), то метка не  исполь-
 
      зуется в транслированном коде и на нее нет ссылок;


                                - 239 -
 
                          00152-01 97 09-1
 
 
           8) функции и подпрограммы,  на  которые  есть  ссылки.
 
      Перечисляются все внешние идентификаторы, на  которые  ссы-
 
      лается исходная программа (кроме ссылок на  идентификаторы,
 
      используемые в качестве формальных параметров) и  их  типы.
 
      Например,  внешние  идентификаторы,  на  которые  ссылается
 
      INCOME, будут распечатаны следующим образом:
 
      FUNCTIONS AND SUBROUTINES REFERENCED
 
      TYPE  NAME               TYPE  NAME
 
      I*4  CLI$GET_VALUE       I*4   REPORT
 
      I*4  FIX_STATS           I*4   CONVERT_FIXES
 
      I*4  GET_STATS                 FOR$OPEN
 
           INCOME_INSFIXVAL          INCOME_FORIOERR
 
           LIB$SIGNAL          I*4   LIB$GET_LUN
 
      I*4  CLI$PRESENT
 
           FOR$CLOSE
 
           INCOME_BADFIXVAL
 
           INCOME_NOACTION
 
           Для получения дополнительной информации об идентифика-
 
      торах в различных секциях карты памяти вместе с  квалифика-
 
      тором /LIST задается квалификатор /CROSS_REFERENCE:
 
      $ FORTRAN/LIST/CROSS_REFERENCE INCOME
 
           Квалификатор /CROSS_REFERENCE включает в карту  памяти
 
      следующую информацию об идентификаторах:
 
           1) строки, где идентификаторы определяются и инициали-
 
      зируются;
 
           2) строки, где значения идентификаторов  модифицируют-
 
      ся;
 
           3) строки, в которых идентификаторы являются фактичес-
 
      кими параметрами;
 
           4) количество появлений идентификатора в каждой  стро-


                                - 240 -
 
                          00152-01 97 09-1
 
 
      ке.
 
           Например, на идентификатор переменной  ADULTS  имеются
 
      четыре ссылки: в строках 24, 36  (где  есть  две  ссылки  и
 
      переменная модифициируется) и 46:
 
      ENTRY POINTS
 
       ADDRESS     TYPE  NAME               REFERENCES
 
      0-00000000    I*4  CALC_SUMS          1         58=
 
      VARIABLES
 
       ADDRESS     TYPE  NAME               REFERENCES
 
      2-0000019C    R*4  ADULTS            24  36(2)= 46
 
      AP-00000018@  R*4  AVG_ADULTS-HOUSE   1  15     46=
 
      AP-0000001C@  R*4  AVG_INCOME_HOUSE   1  15     47=
 
      AP-00000020@  R*4  AVG_INCOME_PERSON  1  15     48=
 
      AP-00000014@  R*4  AVG_PERSONS_HOUSE  1  15     45=
 
           Примечание. В перекрестных ссылках употребляются  сле-
 
      дующие обозначения:
 
           = - модификация значения;
 
           # - определение ссылки;
 
           A - фактический параметр;
 
           D - инициализация данных;
 
         (N) - число ссылок в строке.
 
 
 
           4.4.1.4. Характеристики компиляции
 
 
           Характеристики  компиляции   содержат   квалификаторы,
 
      использованные в команде FORTRAN и  статистику  компиляции,
 
      например:
 
      COMMAND QUALIFIERS
 
      FORTRAN /DEBUG/LIS/MACHINE_CODE CALCSUMS
 
      /CHECK=(NOBOUNDS, OVERFLOW, NOUNDERFLOW)
 


                                - 241 -
 
                          00152-01 97 09-1
 
 
      /DEBUG=(SYMBOLS,TRAGEBACK)
 
      /STANDARD=(NOSYNTAX, NOSOURCE_FORM)
 
      /SHOW=(NOPREPROCESSOR, NOINCLUDE.MAP)
 
      /F77 /NOG_FLOATING /I4 /OPTIMIZE /WARNINGS
 
      /NOD_LINES /NOCROSS_REFERENCE /MACHINE_CODE
 
      /CONTINUATIONS=19
 
      COMPILATION STATISTICS
 
      RUM TIME:       1.29 SECONDS
 
      ELAPSED TIME:   4.02 SECONDS
 
      PAGE FAULTS:    163
 
      DYNAMIC MEMORY: 160 PAGES
 
 
 
           4.4.2. Карты образов
 
 
           Карта образа содержит информацию о процессе компоновки
 
      и получившемся в результате образе; этой картой можно  вос-
 
      пользоваться для локализации ошибок этапа  компоновки,  для
 
      получения расположения образа  в  виртуальной  памяти,  для
 
      слежения за глобальными идентификаторами и т.д. При  компо-
 
      новке программы в интерактивном режиме для генерации  карты
 
      образа необходимо задать в команде LINK квалификатор /MAP:
 
      $ LINK/MAP INCOME
 
           Если программа компонуется в пакетном  задании,  карта
 
      генерируется по умолчанию. Чтобы исключить  в  этом  случае
 
      генерацию карты,  следует  задать  квалификатор  /NOMAP  (в
 
      любом случае нет автоматической печати  карты  образа).  По
 
      умолчанию файл карты имеет тип MAP и имя первого из об'ект-
 
      ных файлов, заданного в команде LINK. Для отмены  умолчания
 
      имя файла задается в команде LINK. Например, генерация фай-
 
      ла карты со спецификацией INCOME1.MAP задается командой:
 


                                - 242 -
 
                          00152-01 97 09-1
 
 
      $ LINK/MAP=INCOME1.MAP INCOME
 
           Компоненты  файла  карты  зависят  от  квалификаторов,
 
      заданных с квалификатором /MAP. Если будет  просто  указано
 
      LINK/MAP, карта образа будет содержать  пять  секций:  если
 
      задать LINK/MAP/FULL, карта будет содержать восемь  секций:
 
      если задать LINK/MAR/BRIEF, карта  будет  содержать  только
 
      три секции. В табл. 8 показано, какие секции карты  генери-
 
      руются по различным квалификаторам команды и по каждой ком-
 
      бинации квалификаторов.
 
                                                        Таблица 8
 
      -----------------------------------------------------------
                     !
        Квалификатор !        Секции карты образа
                     !
      -----------------------------------------------------------
                     !
       /MAP/BRIEF    !    Конспект об'ектного модуля, конспект
                     !
                     ! образа, статистика прогона компоновщика
                     !
       /MAP          !    Конспект об'ектного модуля, конспект
                     !
                     ! образа, статистика прогона компоновщика,
                     !
                     ! конспект программной секции, идентификато-
                     !
                     ! ры по именам
                     !
       /MAP/FULL     !    Конспект об'ектного модуля, конспект
                     !
                     ! образа, статистика прогона компоновщика,
                     !
                     ! конспект программной секции, идентификато-
                     !
                     ! ры по именам, идентификаторы по значениям,
                     !
                     ! конспект   перемещаемых   ссылок  модулей,
                     !
                     ! конспект  секций  образа
 
           Не только полная карта (карта образа,  сгенерированная
 
      по квалификаторам /MAP/FULL) содержит  больше  секций,  чем
 
      краткая карта или карта по умолчанию, но и некоторые секции
 
      полной карты могут содержать больше информации,  чем  такие
 
      же секции карты по умолчанию или краткой карты. Эта  инфор-


                                - 243 -
 
                          00152-01 97 09-1
 
 
      мация касается модулей или разделяемых образов, не  указан-
 
      ных явно в процессе компоновки.
 
           С квалификаторами /MAP и /MAP/FULL можно задать  также
 
      квалификатор /CROSS_REFERENCE, отчего секция "идентификато-
 
      ров по  именам"  заменяется  секцией  "перекрестные  ссылки
 
      идентификаторов".
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


                                - 244 -
 
                          00152-01 97 09-1
 
 
                     Перечень ссылочных документов
 
 
 
      1. Операционная система МОС ВП. Подсистема управления. Диа-
 
         логовый   командный   язык.  Руководство   пользователя.
 
          00152-01 92 19.
 
      2. Операционная система МОС ВП.  Подсистема  исполнительной
 
         библиотеки. Руководство программиста.  00152-01 33 09.
 
      3. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Интерфейсные программы. Руководство  программиста.
 
          00152-01 33 02.
 
      4. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Программы системного обслуживания. Справочный  ма-
 
         териал.  00152-01 97 06.
 
      5. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса.  Системные    программы.   Справочный    материал.
 
          00152-01 97 05.
 
      6. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Управление  вводом-выводом.  Руководство  програм-
 
         миста.  00152-01 33 03.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
                      
 


                                - 244K -
 
                          2б.00152-01 97 09-1
 
 
 
 
 
                     Перечень ссылочных документов
 
 
 
 
      1. Операционная система МОС ВП. Подсистема управления. Диа-
 
         логовый   командный   язык.    Руководство    оператора.
 
         2б.00152-01 34 01.
 
      2. Операционная система МОС ВП.  Подсистема  исполнительной
 
         библиотеки. Руководство программиста. 2б.00152-01 33 09.

      3. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Интерфейсные программы. Руководство  программиста.
 
         2б.00152-01 33 02.
 
      4. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Программы системного обслуживания. Справочный  ма-
 
         териал. 2б.00152-01 97 06.
 
      5. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса.  Системные    программы.   Справочный    материал.
 
         2б.00152-01 97 05.
 
      6. Операционная система МОС ВП. Подсистема системного  сер-
 
         виса. Управление  вводом-выводом.  Руководство  програм-
 
         миста.  2б.00152-01 33 03.
 
 
 
 


 
 
                               - 245K -
    
                          2б.00152-01 97 09-1
    
    -----------------------------------------------------------
    I               Лист регистрации изменений                I
    -----------------------------------------------------------
    I Номера листов (страниц)IВсего I       IВходяшийI    I   I
    I------------------------IлистовI    N  I    N   I    IДа-I
    I   IИз- IЗа- I   IАнну- I(стра-I       Iсопрово-IПодпIта I
    IИзмIме- Iме- IНо-Iлиро- Iниц) вIдокуменIдительноI    I   I
    I   Iнен-Iнен-IвыхIванных[5~кум.I   та  Iго докумI    I   I
    I   Iных Iных I   I      I      I       I и дата I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------
    I   I    I    I   I      I      I       I        I    I   I
    -----------------------------------------------------------


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!