Глава 4. Процессы.

4.1. Понятие процесса и его параметры.

В этом разделе мы остановимся не на особенностях языка KLisp, а на вариантах построения интерпретатора этого языка и настройке его параметров в зависимости от требований пользователя. Эта возможность появляется при поставке системы в открытом виде, как библиотеки С++ и исходного текста main.cpp, что позволяет строить порождаемые от Serv_KL классы, вводить новые операторы и переопределять существу- ющие. Поскольку процедурный интерпретатор KLisp реализован как отдельный класс С++, появляется возможность легкого построения моногопроцессных программных систем, то есть одновременного выполнения нескольких,вза- имодействующих между собой KLisp программ. Условимся процессом называть экземпляр класса Serv_KL или им по- рожденного класса. Размер массива этого класса задается в исходном тексте main.cpp при сборке интерпретатора транслятором С++ Serv_KL Sv_KL[Len_proc]; Len_proc - определяет количество процессов,одновременно обслуживаемых интерпретатором. Каждому такому классу ставиться в соответствие некоторый набор ресурсов, которыми он пользуется. Порождаемыми классами этот набор может расширяться и изменяться, но основными ресурсами являются сле- дующие: - Внешний буфер памяти и класс, его обслуживающий. Размер этого буфера определяет размер пространства для загрузки KLisp программ в данный процесс.Для каждого экземпляра класса он должен быть индивиду- альным. - Класс, обслуживающий взаимодействие с пользователем. Обычно это вывод на экран и ввод с клавиатуры. Он может быть один для всех про- цессов, либо для каждого свой, если используются разные устройства ввода/вывода. - Внутренний буфер обмена данными, через который процессы могут обмениваться между собой информацией. Все эти ресурсы задаются в main.cpp перед трансляцией. Здесь же определяется цикл и последовательность передачи управления процессам. При передаче управления процессу,он выполняет один оператор и возвра- щается в main. В зависимости от требований пользователя, можно либо обходить все процессы последовательно, либо устанавливать приоритеты исполнения. Для контроля параметров процессов существует информационный опе- ратор (info ). (int Par)(info (char Rev) ) ------------------------------------ (char Rev) - номер параметра ) - номер процесса. По умолчанию текущий. Каждому процессу при инициализации присваивается определенный номер начиная с 1. Указывая аргумент Pr в операторе, можно из одного процесса запрашивать параметры других. Этот же номер используется и в других операторах межпроцессорного взаимодействия. Если Pr не указан, возвращаются параметры того процесса в котором выполняется программа, вызвавшая этот оператор. Возвращает значение параметра. Глобальные ошибки: - Ошибка типа аргументов. - Ошибка номера процесса. Оператор (info ), расположен в классе DOS_KL,и является открытым. Hомера параметров условны, и их список может быть расширен для других порождаемых классов. В данной реализации принята следующая нумерация: Rev==1 - возвращает значение: Len_proc - число процессов открытых в интерпретаторе. Rev==2 - возвращает номер текущего процесса. Rev==10 - возвращает значение: Mode - состояние процесса. Любой процесс может находиться в различных состояниях в зависимости от тре- бований программы: Mode==0 - процесс не исполняется. То есть никакая программа процесса не активизирована.Цикл передачи управления обходит этот про- цесс. В этом состоянии процесс находиться в том случае,если в него не загружена никакая программа, либо программы загружены, но не запущены на выполнение оператором (run ). Mode==1 - процесс исполняется. То есть исполняются операторы загруженых в этот процесс программ при передаче ему управления. Mode==7 - процесс находиться в состоянии ожидания. То есть в момент исполнения программы, процесс был остановлен до совершения какого-либо события. Этим событием может быть либо завершение другого процесса, номер которого храниться в параметре Wait (Rev==14), либо принудительный запуск из другого процесса оператором (run ). Rev==14 - возвращает значение: Wait - номер ожидаемого процесса. Rev==11 - возвращает значение: END_DN - конец загруженых в про- цесс программ. С этого адреса загружаются следующие программы. Rev==12 - возвращает значение: FREE_DN - размер свободного прос- транства процесса между END_DN и концом стека программных вызовов для загрузки новых программ. Rev==13 - возвращает значение: ERR_KL - код глобальной ошибки процесса. Процесс с номером 1 является управляющим. В него загружается код KLisp программы указанной в командной строке, переданной интерпрета- тору: klisp prob.kl После загрузки управление передается первой программе этого процесса. Все остальные процессы имеют режим Mode == 0. Дальнейшее управление загрузкой и инициализацией других процессов производиться внутри ин- терпретатора. Завершение программ в первом процессе означает оконча- ние работы интерпретатора.

4.2. Операторы загрузки и выгрузки программ.

В эту группу входят два оператора (load ) и (dload ). Поскольку оттранслированные KLisp программы помещаются в файлы DOS, то и реали- зованы эти операторы в системозависимом классе DOS_KL. Класс Serv_KL предоставляет только механизм редактирования списка подпрограмм. Оператор загрузки программ: (char ERR)(load (char[] Nam_fil) ) -------------------------------------------- (char[] Nam_fil) - имя файла программы ) - номер процесса. По умолчанию текущий. Производиться загрузка программы из файла в указанный процесс, в пространство между концом последней программы (параметр END_DN) и вершиной стека программных вызовов. Возвращает 0 при успешном завершении или код ошибки: 1 - нет файла. 2 - ошибка в данных. 3 - ошибка номера процесса. 4 - ошибка структуры файла. 5 - не хватает памяти для загрузки. >10 - ошибка списка подпрограмм. Глобальных ошибок нет. Как уже отмечалось, любая оттранслированная KLisp программа может быть загружена в "хвост" любого процесса, независимо от его адреса. После загрузки обращение к новым подпрограммам производится либо по имени, либо через переменные (func ) после инициализации их адресами новых подпрограмм оператором (setq ). Внимание, в данной реализации интерпретатора не производится контроль совпадения имен загружаемых подпрограмм. Оператор выгрузки "хвоста" подпрограмм, (char ERR)(dload (char Rev) (char Pr) (func Fn) ) ----------------------------------------------------------------- (char Rev) - режим выгрузки. (char Pr) - номер процесса, без умолчания. 0 - текущий процесс. (func Fn)/(char[] Fn) - номер или имя подпрограммы выгрузки. - имя файла выгрузки. Режим выгрузки определяет, что делать с "хвостом": Rev==0 - освободить память процесса; Rev==1 - Выгрузить в файл с очисткой памяти; Rev==2 - Выгрузить в файл без очистки памяти. Выгрузка в файл требует наличия параметра Nam_fil.При этом форми- руется загружаемый файл KLisp программ, находящихся в процессе, начи- ная с программы указанной Fn, до конца. При требовании, очистить па- мять, параметр "хвоста" процесса - END_DN смещается к концу последней сохраненной в памяти программы. Сформированный файл может быть снова загружен оператором (load ), при этом значения всех переменных сохра- няется. Однако при повторной загрузке программ с другого адреса сле- дует заново инициализировать переменные (addr ) и (func), так как они зависят от адреса загрузки. Файлы, созданные оператором (dload ), мо- гут загружаться и непосредственно интерпретатором KLisp. Это свойство можно использовать для независимого от транслятора формирования ис- полняемых файлов и предварительной настройки их параметров, но при этом следует помнить, что исполнение при загрузке из командной строки начинается с первой в списке подпрограмм выгруженных в файл. Следует обратить внимание на то, что все подпрограммы оттрансли- рованные из одного текстового файла выгружаются одновременно, даже если Fn указывает не на первую из них. Это обстоятельство следует учитывать при объединении подпрограмм при трансляции. Оператор возвращает 0 при успешном завершении или код ошибки: 1 - ошибка структуры хвоста 2 - ошибка в данных. 3 - ошибка номера процесса. 4 - ошибка записи в файл. >10 - ошибка списка подпрограмм. Глобальных ошибок нет. В заключении следует отметить что операторы (load) и (dload), как и многие другие описанные операторы, могут быть полностью реализованы средствами самого языка KLisp, все дело только в скорости исполнения.

4.3. Операторы взаимодействия процессов.

Эти операторы относятся к внутренним операторам класса Serv_KL и играют очень большую роль при реализации на KLisp многопроцессных программных комплексов. Они отвечают за запуск и остановку программ в параллельных процессах. Такая возможность появляется благодаря тому что управление процессам передается последовательно, то есть в момент исполнения оператора из текущего процесса, остальные находятся в сос- тоянии ожидания и изменение их параметров не вызывает конфликтов при исполнении. Практически работа этих операторов сводится к манипуляции флагов Mode и Wait класса Serv_KL описанных в п.4.1. Итак, оператор (run ) - запустить программу в работу. (char ERR)(run (char Rev) (char Pr) <... Fn>) --------------------------------------------- (char Rev) - состояние вызывающего процесса после запуска: 0 - Ждать завершения работы в другом процессе, в вызвавшем процессе устанавливается флаг Wait=Pr; 1 - Без ожидания (параллельно). (char Pr) - номер процесса, где должна быть запущена программа. Без умолчаний. (char[] Fn) (func Fn) - номер или имя вызываемой подпрограммы. Оператор (run ) запускает на исполнение подпрограмму из процесса Pr , предварительно загруженую в него оператором (load ), то есть в этом процессе устанавливается флаг Mode=1 (см. п. 4.1.). После этого, в вызвавшем процессе устанавливается флаг ожидания Mode=7,если Rev=0, либо он продолжает исполняться параллельно Rev=1. Если процесс Pr до этого находился в состоянии ожидания - Mode=7, аргумент Fn не указы- вается и процесс продолжает интерпретацию кода с места остановки.Если же процесс Pr до этого не был активизирован - Mode=0, оператор (run ) ищет в нем подпрограмму Fn и активизирует ее сначала. При этом ника- ких параметров ей не передается. Значение (func Fn) в параллельном процессе может быть получено оператором (setq ) (см. п. 3.7.) с указанием номера процесса: (setq (func Fn) (char[] Nam_Fn) (char Pr)) Оператор (run ) возвращает 0 при успешном завершении или код ошибки: 1 - ошибка номера процесса. 2 - ошибка указания подпрограммы. 3 - ошибка режима процесса. 4 - ошибка типов данных. Глобальных ошибок нет. Установить режим ожидания процесса: (char ERR)(wait (char Pr) (char No)) ------------------------------------ После исполнения оператора флаг Wait процесса Pr будет равен No, флаг Mode процесса Pr равен 7. Процесс Pr встает на ожидание до за- вершения процесса No, либо до активизации его оператором (run ) из другого процесса. Оператор возвращает 0 при успешном завершении или код ошибки: 1 - ошибка номера процесса No или Pr. 3 - ошибка режима процесса. Mode Pr не равно 1. 4 - ошибка типов данных. Глобальных ошибок нет. Завершить процесс: (non)(exit ) --------------------- Флаг Mode процесса Pr устанавливается равеным 0. По умолчанию за- вершается текущий процесс. Стек программных вызовов очищается. Завер- шение первого процесса означает завершение работы всего интерпрета- тора. Глобальных ошибок нет. 4.4. Операторы, обслуживающие буфер обмена процессов. Эти операторы были введены для того,чтобы процессы могли общаться между собой непосредственно внутри интерпретатора не обращаясь к средствам операционной системы. Хотя они и реализованы в классе DOS_KL, сделано это лишь для того, чтобы повысить открытость системы. Принцип их прост: в каждом экземпляре класса объявляется буфер int BUFKEY[LENKEY]; Размер этого буфера в данной реализации равен 256, но может быть из- менен при перетрансляции интерпретатора, в зависимости от решаемых на языке KLisp задач. Данные в этот буфер заносятся в режиме стека: первый пришел - последним вышел. Такой принцип более удобен, так как позволяет накапливать сообщения постепенно, не боясь конфликтов, ну, а как их обрабатывать, это уже задача программ. Отметим, что этот же буфер используется и для передачи командной строки из операционной системы при загрузке интерпретатора: klisp prob.kl comand при этом программа prob.kl загружается в первый процесс интерпрета- тора, а строка "command" в буфер обмена этого процесса, как обрабаты- вать эту строку зависит от задачи. Операторов обмена четыре: Запись кодов в буфер. (non )(push (char Pr) (ch\in<[]> Key) ) ---------------------------------------------------- (char Pr) - Hомер процесса в буфер которого производиться запись. Если Pr=0 - в буфер текущего процесса. (ch\in<[]> Key) - Переменная (массив) языка KLisp - источник кода - Количество выбираемых из массива знаков. Оператор записывает коды в буфер указанного процесса в режиме стека. Если Key не массив, или Dim отсутствует, то записывается один знак. Вершина стека увеличивается на Dim. Глобальные ошибки: - ошибка номера процесса; - ошибка типов данных. Чтение кодов из буфера. (int len)(pop (char Pr) (ch\in<[]> Key) ) ---------------------------------------------------- (char Pr) - Hомер процесса из буфера которого производится чтение. Если Pr=0 - из буфера текущего процесса. (ch\in<[]> Key) - Переменная (массив) языка KLisp - приемник кода - Количество выбираемых из буфера знаков. Оператор читает коды из буфера указанного процесса. Если Key не массив, или Dim отсутсвтует, то считывается один знак в режиме стека. Если производится считывание нескольких знаков в массив, заполнение его начинается с конца. Таким образом загруженая в буфер строка вос- станавливается. Hапример, для чтения приведенной выше командной стро- ки достаточно одного оператора (pop ) (char Com[80]) (pop 0 Com 80) (msg Com) // Hапечатает "comand" Если же считывать буфер по одному знаку, выбор начнется с 'd'. После выполнения оператора вершина стека уменьшается на Dim. Оператор контролирует размер принимающего массива и количество знаков в буфере и возвращает число реально считанных знаков. Глобальные ошибки: - ошибка номера процесса; - ошибка типов данных. Число знаков в буфере обмена. (int len)(lenkey ) --------------------------- (char Pr) - Hомер процесса. По умолчанию текущий. Очистить буфер обмена процесса. (non )(flush ) ----------------------- (char Pr) - Hомер процесса. По умолчанию текущий. P.S. Главы 4. В отличии от операционных систем, реализующих многозадачный режим работы средствами ассемблера, многопроцессный режим работы принятый в KLisp более прост,более нагляден и гибок. Он дает в руки пользователя мощный инструмент организации взаимодействия между параллельными про- цессами не только по обмену данными, но и по управлению исполнением.