Господа, прошу прощения опять... Все время появляюсь в роли ламера, который никак не может перейти в более серьезную категорию Просто думал я, что буду с большой машиной много дел иметь, а меня на другой фронт кинули... А сейчас понадобилось некое API реализовать, потому как те кто собирался его делать - не справляются, а я другой фигней страдал, "опыта набраться" не было возможности, и даже мануалы вдумчиво проработать некогда было Короче, нужны практические подсказки. Вот я делаю програмку, выглядит так:
testapi: package exports(fun1,fun2,...); test: proc options(main); /*голова для теста этого API*/ ... end test; fun1: proc(...) options(task); /*пошел собственно API*/ ... end fun1; fun2: proc(...) options(task); ... end fun2; dcl var1 ... static ; /* для сохранения всякой ерунды между вызовами */ dcl var2 ... static ; end testapi ;
Правильно ли В ОБЩЕМ я делаю ? Я ни разу не PLщик, терпеть его не могу... Практические вопросы: При записи значения в какую-то из static-переменных внизу получаю 0C4. Что я сделал не так ? А как при использовании оператора package указывается основная точка входа ? (Борюсь с желанием все сделать на ассемблере... Но меня не поймут! )
Далее: работать приходится с IP. И вот EZASOKET(INITAPI,...) загадывает загадки: Как я могу определить делал ли кто-то до меня инициализацию IPшности ? Дело в том, что я наткнулся (случайно) на ситуацию: по ошибке написал какой-то невинный вызов, что-то вроде GETHOSTBYNAME до INITAPI. Естественно, там получил плохой код возврата, но после этого INITAPI тоже выдавала какой-то хреновый код возврата! И непонятно как эту ситуацию разруливать ... У кого-нибудь есть опыт в этих делах ? Если будет 2 вызова INITAPI (один мой. один не мой) - это смертельно ? Если да - как этого избежать ?
Да, с ПЛ-щиками тут у нас туго... Среди системщиков мало кто на ПЛ пишет, я 15 лет назад писал, и тогда никаких сокетов и TCPIP не было. Так вот 0C4 вылетал при передаче управления не туда или при работе с неинициализированными переменными.
Приветствую. Прежде всего мне у Вас кое что непонятно: fun1: proc(...) options(task); /*пошел собственно API*/ <-- зачем здесь option(task) ? fun2: proc(...) options(task); <-- зачем здесь option(task) ? По поводу dcl var1 ... static ; /* для сохранения всякой ерунды между вызовами */ и test: proc options(main); /*голова для теста этого API*/ чтобы это прокомментировать, нужно знать как Вы собираете загрузочный модуль/программный объект и как он вызывается. "Я ни разу не PLщик, терпеть его не могу... " -- так а что же Вы на C/C++ не пишете? IMHO языки с типизацией (PL/1) вообще глупость есть, а со строгой типизацией (C/C++) так просто кретинизм...
Проблемы с инициализацией, как я полагаю, из-за того, что сокет не был закрыт. Последовательность действий выглядит так:
rc = SOCKET('INITIALIZE','SD') parse value SOCKET('Socket','AF_INET','SOCK_STREAM') with rc sd . ... rc = SOCKET('CONNECT',sd,'AF_INET 'port' 'host) ... rc = SOCKET('SetSockOpt',SD,'Sol_Socket','So_Ascii','On') ... rc = SOCKET('SetSockOpt',SD,'Sol_Socket','So_KeepAlive',1) ... rc = SOCKET('SEND',sd,'GET 'query''cr''lf' HTTP/1.0'cr''lf''cr''lf) rc = SOCKET('SELECT','READ * WRITE * EXCEPTION *',5) response = '' do forever parse value SOCKET('RECV',sd,1024,'PEEK') with rc size data ... end rc = SOCKET('CLOSE',sd)
а со строгой типизацией (C/C++) так просто кретинизм...
Что есть типизация в данном контексте? С по архитектуре схож с ассемблером, команды последнего тоже строго привязаны к типам данным. Тогда, весь z/Series кретинизм Худая корова еще не газель!
z/Series или Wintel здесь совершенно не при чем, не путайте аппаратные и абстрактные типы данных. Типизация в языках программирования есть наличие деклараций, указывающих компилятору как ему следует обращаться с данными, например, DCL B BIN FIXED(31) в PL/1 или LONG B в C/C++. В языках со строгой типизацией все данные должны быть декларированы и преобразования типа должны делаться только явно, при нестрогой типизации (PL/1) используются неявные описания и преобразования. При этом типы вовсе не обязательно соответствуют аппаратным типам... Мне нравятся языки без типизации, как REXX, shell, PERL, поскольку они позволяют писать более кратко.
Приветствую. Прежде всего мне у Вас кое что непонятно: fun1: proc(...) options(task); /*пошел собственно API*/ <-- зачем здесь option(task) ? fun2: proc(...) options(task); <-- зачем здесь option(task) ?
Х его З, товарищ майор ... пардон, сержант! Строго говоря, хрень должна быть многозадачной. Поскольку я PL не знаю, предполагаю что options(task) указывает компилятору на необходимость делать процедуру реентерабельной.
Quote
чтобы это прокомментировать, нужно знать как Вы собираете загрузочный модуль/программный объект и как он вызывается.
В данном случае это была попытка все до кучи подать в sysin dd * (Возможно что это плохая идея, что надо было сделать б-ку и компилить функции по одной...)
Quote
"Я ни разу не PLщик, терпеть его не могу... " -- так а что же Вы на C/C++ не пишете? IMHO языки с типизацией (PL/1) вообще глупость есть, а со строгой типизацией (C/C++) так просто кретинизм...
PL - требование заказчика. Насчет типизации - у меня голова строго наоборот устроена: хочу четко знать с какими данными работаю, а не оставлять этот вопрос на милость компилятора. Я тут охренел от того же PLя (это месяца 3 назад было, уже неточно помню) : написал конструкцию вида примерно (N/1000)*1000 ... какие значения может принимать результат ? Все подумали как следует .... ... а 250 не хотите ли ? Вот так, блин, бывает когда компилятор решает каким будет тип промежуточной переменной ...
Quote
Проблемы с инициализацией, как я полагаю, из-за того, что сокет не был закрыт. Последовательность действий выглядит так:
Никто сокет не открывал. Повторяю, был РАБОТАЮЩИЙ пример, в котором работа с ipшностью начиналась, как и положено, с EZASOKET(INITAPI,...). А потом совершенно случайно вставил какой-то вызов типа gethostbyname ПЕРЕД initapi. Просто за пальцами не уследил. А в результате - initapi стал выдавать какой-то дикий код возврата, и непонятно что в такой ситуации можно сделать...
options(task) указывает компилятору на необходимость делать процедуру реентерабельной.
TASK указывет на использование мультизадачности (вызов CALL ... TASK), причем наличие OPTION(TASK) является чисто декоративным: "This option is specified to document that the PL/I multitasking facilities are being used. Otherwise, the TASK option need not be specified. The CALL statement with the TASK option is sufficient to invoke PL/I multitasking". Для реентерабельности процедуры указывается OPTIONS(REENTRANT). У Вас multitasking или multithreading предполагается вообще?
Quote
В данном случае это была попытка все до кучи подать в sysin dd *
Если я Вас правильно понял, у Вас строится один загрузочный модуль который вызывается обычным образом (EXEC PGM=main)?
Quote
... когда компилятор решает каким будет тип промежуточной переменной
см. режим RULES и определите по своему вкусу, отменив умолчания. В отличие от C/C++ в PL/1 правилами языка можно управлять. Кстати, какой PL/1 компилятор используется, PL/1 enterprise? (см. заголовок листинга)
Для реентерабельности процедуры указывается OPTIONS(REENTRANT). У Вас multitasking или multithreading предполагается вообще?
Ага, один значит человек все-таки PL знает Интересный вопрос: "пригодность процедуры к многозадачности" и реентерабельность - это не одно и то же, ежели по сути ? Что касается мультитредовости, то тут я впадаю в ступор: мыслю категориями S360/370, там таких слов не было А что, нынче появилось ?
Quote
Если я Вас правильно понял, у Вас строится один загрузочный модуль который вызывается обычным образом (EXEC PGM=main)?
Это был тест. А в принципе, надо будет отдать людям job, который им создаст библиотеку. Потому что кадры там еще те, не факт что они сами такой джоб напишут
Quote
Кстати, какой PL/1 компилятор используется, PL/1 enterprise? (см. заголовок листинга)
Интересный вопрос: "пригодность процедуры к многозадачности" и реентерабельность - это не одно и то же, ежели по сути ?
P: proc options(main) ... call P1 task call P2 task ... end P1: proc option(task) ... P2: proc option(task) получаем multitasking: P создает подзадачу P1 и подзадачу P2. P: proc options(main) ... attach P1 ... attach P2 ... ... end P1: proc ... P2: proc получаем multithreading (см. подробности в SC27-1460-01 Enterprise PL/I for z/OS and OS/390 Language Reference Chapter 18)
reentrant же позволяет получить реентерабельный код, который может вызываться откуда угодно. То есть reentrant, mutitasking и multithreading это несколько разные вещи...
Quote
у Вас строится один загрузочный модуль?... надо будет отдать людям job, который им создаст библиотеку
Вы на мой вопрос так и не ответили... Загрузочный модуль/программный объект, естественно, должен находиться в библиотеке, загрузочный модуль - в PDS, программый объект - в PDSE. Я же пытаюсь понять, что представляет собой Ваш исполнимый код - простую структуру (один загрузочный модуль или программый объект) или динамическую (несколько модулей) и каким образом он доступен пользователю - он самодостаточен и может вызываться непосредственно или должен подключаться к программе пользователя динамически либо статически? Посмотреть, какой компилятор используется проще всего заглянув в первую строку его листинга: там указывается номер и наименование программного продукта: - 15688-235 IBM PL/I for MVS & VM Ver 1 Rel 1 Mod 1 with MLE - 15655-H31 IBM® Enterprise PL/I for z/OS V3.R1.M0 (Built:20020422)
Сообщение отредактировал Gregory - Вт, 11.08.2009, 12:25
reentrant же позволяет получить реентерабельный код, который может вызываться откуда угодно. То есть reentrant, mutitasking и multithreading это несколько разные вещи...
С вашего позволения расширю ответ.
Реентерабельная программа - это программа которая НЕ МОДИФИЦИРУЕТ свое тело. Изменение любой переменной - это модификация. Как же работать? Просто такая программа для всех переменных запрашивает динамически область памяти и уже там все модифицирует. Некоторые макро тоже пишутся специальным образом. Неизменяемая часть макро в программе и изменяемая в области, и ссылка от одной к другой. В результате системе достаточно хранить всего одно тело программы в памяти хоть для сотни запусков. Тело не меняется, а все переменные у каждого в своей области. Более того, это тело помещается в область памяти защищенную от записи. Обманешь - будет абенд.
Тоесть, совсем не многозадачность. Просто при запуске сотни однояйцевых задач, получишь экономию памяти и немного скорости(для каждого следующего запуска не надо модуль читать с диска, грузить в память и тд).
ОК, отвлечемся от основной темы и порассуждаем немножно о реентерабельности... Господа, я, конечно, PL не знаю, и с z/OS дел имел очень мало, но все-таки родился не вчера, и немножко поработать успел Будьте так добры привести мне примеры а) РЕЕНТЕРАБЕЛЬНОЙ программы, которую НЕЛЬЗЯ вызывать из двух задач б) НЕРЕЕНТЕРАБЕЛЬНОЙ программы, которая при этом может использоваться несколькими задачами без побочных эффектов ...
Сам я таких примеров придумать не могу, посему для меня "пригодность к многозадачному использованию" и "реентерабельность" есть *почти* синонимы. Если у вас таких примеров есть - давайте их в студию, поговорим !
(Кстати, определение "реентерабельная - та, которая не модифицирует свое тело" - неверно. То, что модуль, имеющий флажок reenterable грузится в защищенную память - это я в курсе, но это не основание давать такое определение. Я сам писал программы, модифицирующие свое тело, и при этом реентерабельные по сути)
ну что ж, давайте порассуждаем... Во-первых, я не утверждал, что реентерабельность никак не связана с многозадачностью, а выразился несколько иначе
Quote
То есть reentrant, mutitasking и multithreading это несколько разные вещи...
Конечно, реентерабельность и многозадачность/многопотоковость связаны. НО...
Quote
Будьте так добры привести мне примеры а) РЕЕНТЕРАБЕЛЬНОЙ программы, которую НЕЛЬЗЯ вызывать из двух задач
А любую программу можно вызвать из двух задач, даже не повторно используемую (reusable).
Quote
б) НЕРЕЕНТЕРАБЕЛЬНОЙ программы, которая при этом может использоваться несколькими задачами без побочных эффектов ...
А вот здесь сначала уточним, что же такое рееентерабельность. Формально реентерабельность это атрибут загрузочного модуля/программного объекта, который присваивается program binder (редактором связей) без всякой проверки вообще. Фактическая же реентерабельность есть свойство программного кода, позволяющее одной и той же его копии в памяти выполняться одновременно несколькими процессами. Таким образом, понятие многозадачности присутствует в самом определении реентерабельности и нельзя говорить, что они не связаны. И я не могу согласиться с определением XOpen
Quote
Реентерабельная программа - это программа которая НЕ МОДИФИЦИРУЕТ свое тело
потому что оно совсем неточно (тело это, скорее, анатомический термин) и не совсем верно: ... ENQ ... MVI ... DEQ ... ...
Выполнение программы, объявленной рееентерабельной, но фактически таковой не являющейся приведет к непредсказуемым последствиям (наиболее вероятно, к аварийному завершению задачи, но вовсе не обязательно).
Сообщение отредактировал Gregory - Вт, 11.08.2009, 18:03
можно родиться хоть завтра, но для начала неплохо было бы найти хоть какое определение в документации, прежде чем спорить. Вот из IBM : Reentrancy allows more than one user to share a single copy of a load module. If your application is not reentrant, each application that calls your application must load a separate copy of your application. The following routines must be reentrant:
Routines to be loaded into the LPA or ELPA Routines to be used with CICS Routines to be preloaded with IMS
Your routine should be reentrant if it is a large routine that is likely to have multiple concurrent users. Less storage is used if multiple users share the routine concurrently. Reentrancy also offers some performance enhancement because there is less paging to auxiliary storage.
If you want your routine to be reentrant, ensure that it does not alter any static storage that is part of the executable program; if the static storage is altered, the routine is not reentrant and its results are unpredictable.
Как программа модифицирующая свое тело может быть рент по сути ??? Это программа без флажка, но ничего не меняющая может быть по сути. IEFBR14
По вашим пунктам: а) рент-программы и существуют для того, чтобы их вызывали много и из разных мест. б) с любым механизмом синхронизации. ENQ\DEQ, семафоры, очереди и тд.
Программа считающая 2+2=4 может быть реентерабельной. Но никто не назовет ее многозадачной, многопоточной и тд. Вам ведь на эту разницу указали.
см мой комент. НЕ МОЖЕТ ! ENQ\DEQ скорее тянет на REUSABLE
Да и про тело ты не прав. Тело программы - это распространенный компьютерный термин. Есть еще и тело процедуры и тело цикла. На пару моргов точно хватит тел в программировании.