分类:
2006-05-29 14:44:23
第三章 程序和过程间的流程控制及通讯
可用CALL、CALLPRC和RETURN命令来在程序和过程之间传递控制,每个命令都有不同的特点。在传递控制时会把要传递的信息做为参数传给被调用程序。
特别要注意的是用CALL或CALLPRC运行用USRPRF(*OWNER)生成的程序,这与在主人用户配置文件下运行的程序处理有明显的不同安全特性。这章包括一般使用的程序设计接口(GUPI),可用在客户写的程序中。
3.1 CALL命令
CALL命令调用规定程序且把控制传给它,格式如下:
CALL PGM(库存名/程序名) PARM(参数值)
程序名和库名可以是变量。如果被调用的程序所在的库不在库列表中,必须在PGM参数中规定库名。PARM参数在3.4.4中介绍。当被调用的程序完成运行,控制返回给调用程序的下一条命令。
PGM DCL… CALL PROGB SNDPGMMSG… ENDPGM PGM DCL… CHGVAR… ENDPGM
程序中一些CALL命令的互相调用叫做调用堆栈。例如:
CALL PROGB ENDPGM ENDPGM CALL PROGC ENDPGM
调用堆栈是:
返回 调用
PROGA |
PROGB |
PROGC |
在PROGC完成操作时,控制返回到PROGB中调用PROGC的后一条命令,控制在堆栈中向上返回。无论PROGC用RETURN或ENDPGM结束都这样。CL程序也能调用自身。
3.2 CALLPRC命令
CALLPRC命令调用规定的过程且把控制传给它。格式如下:
CALLPRC PROCEDURE(过程名) PARM(参数值) RTNVAL(返回值变量)
过程名不能是变量,PARM参数在3.4中介绍,当调用过程做完控制返回到调用过程的下一条命令。
PGOMA
PROGA PROGB PGM DCL… CALLPRC PROGB SNDPGMMSG ENDPGM PGM DCL… CHGVAR… ENDPGM
过程中CALLPRC互相调用的顺序叫做调用堆栈,例如:
PGMA SRVPGMA
PROCA PROCB CALLPRC PROGB ENDPGM CALLPRC PROGC ENDPGM PROCC ENDPGM
返回 调用
|
PROCB |
PROCC |
在PROCC完成操作时,控制返回到PROCB中调用PROCC的后一条命令,控制在堆栈中向上返回,无论PROCC用RETURN或ENDPGM结束都这样。CL过程也能调用自身。
3.3 RETURN命令
CL过程中或OPM程序中的RETURN从调用堆栈中取消过程或OPM程序。如果用CALLPRC命令调用有RETURN的过程,控制返回到调用程序的CALLPRC命令后的下一条语句。
如果MONMSG命令规定了一个用RETURN结束的动作,控制返回到调用过程的下一条语句或有MONMSG命令的程序。
RETURN设有参数。
注:如果在初始程序中用RETURN给出命令入口显示,出于安全考虑要避免发生这
种情况。
3.4 在程序和过程之间传递参数
在把控制传给另外的程序或过程时,也能把修改或使用的信息传给接收的程序或过程,可在CALL或CALLPRC命令中的PARM规定要传送的信息。
如果PROGA有下面命令:
CALL PROGB PARM(&AREA)
它调用PROGB且把值&AREA传送给它,PROGB必须以PGM开始,且必须一定要有接收的参数:
PGM PARM(&AREA) /*PROGB*/
PROGA
PGM DCL &AREA *CHAR 10 CALL PROGB PARM(*AREA) ENDPGM
VALUE
PROGB
PGM PARM(*AREA) /*PROG B*/ DCL &AREA *CHAR 10 ENDPGM
CONTROL
对这两个命令必须要有PARM参数,且在接收程序中的PGM中也要规定PARM参数。由于参数是按位置而不是按名字传送的,所以在CALL 和CALLPRC中传送值的位置必须和接收的PGM中的位置相同。例如,PROGA中有下列命令:
CALL PROGB PARM(&A &B &C ABC)
它传送三个变量和一个字符串。如果PROGB用下列命令开始:
PGM PARM(&C &B &A &D) /*PROGB*/
那么PROGA中的&A就传给PROGB的&C。依此类推,&D即为ABC,而PROGB中DCL语句的顺序并不重要,仅是在PGM语句中规定的参数顺序决定变量传送的顺序。
对于位置参数,也要注意它们的长度和类型。在接收程序中列出的参数必须说明为与调用程序同样长度和类型,十进常量总是用(15 5)的长度传送。
在使用CALLPRC命令且传送字符串常量时,要规定确切的字节数且传送这个数。被调用的过程可用这些操作描述符中的信息确定传送的确切字节数,也可用API CEEDOD来访问操作描述符。
在使用CALL命令时,长度小于或等于32字节的字符串常量总以32字节传送。如果长度大于32,必须给出确切的字节数且传送这个数。
CALLPRC在执行过程中不检查传送的参数是否与期望值匹配,这就让程序员可以灵活的使用变量长度参数列表来编程。例如,在后来规定的参数列表中的参数不需要根据前面参数列表中值来确定,这也适应用其它ILE语言写的过程及调用CL过程。下面是一个接收&VAR1的过程:
PGM PARM(&VAR1) /*PGMA*/
DCL VAR1 *CHAR LEN(36)
.
.
.
ENDPGM
CALL或CALLPRC命令必须规定36个字符:
CALLPRC PGMA(ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ)
下列例子规定缺省长度:
PGM PARM(&P1 &P2)
DCL VAR(&P1) TYPE(*CHAR) LEN(32)
DCL VAR(&P2) TYPE(*DEC) LEN(15 5)
IF (&P1 *EQ DATA) THEN(CALL MYPROG &P2)
ENDPGM
要调用此程序,规定 CALL PROG(DATA 136)
则字符串DATA传给&P1,十进值136传给&P2。
引用本地定义的变量要比引用传送的变量花费要少。这样,被调用程序如果频繁地引用传送来的变量,就不如把传送的值复制到本地变量中来引用,这样性能要好一些。
在调用用一个OPM CL程序时,传送给它的参数数量必须确切的与接收程序预计的数量一致。这个预计值是由程序生成时确定的(操作系统要限制你调用与被调用程序的参数不一致)。在调用一个ILE程序或过程时,操作系统不检查调用与被调用的参数的数量。另外,操作系统存储参数的空间在程序调用时并不重新初始化。如果预计有n个参数的过程调用了n-1个参数,它将用参数空间的值来访问第n个参数。这样,结果是不可预测的,这也适合用其它ILE语言写的调用CL过程的程序,或由CL过程调用的过程。
这给用户写ILE CL过程的更大的灵活性。这是因为可以写有变量长度参数列表的过程。例如,根据一个参数的值,可不需要在此列表后面规定它,如果指出的控制参数是一个没规定的可选参数,那么被调用过程就不去引用 这个可选参数。
可用*OMIT来规定从参数列表中去掉某些参数。这时,被调用的过程传送空指针。在CL语言中,可在第一次引用去掉的参数时监控MCH3601信息来检查空指针。在过程中要规定有MCH3601时要采取的动作。
下面的例子中有二个CL过程,第一个预计有一个参数,如果参数不是期望值,则结果不可预测,第一个过程调用第二个过程PROC1。PROC1预计有一个或二个参数,如果第一个参数为1,则希望规定第二个参数,如果第二个参数为‘0’,假定第二个参数为非预计值而使用缺省值。PROC1也用API CEEDOD来确定从第二个参数传来的实际长度。
MAIN: PGM PARM(&TEXT)/* 必须规定&TEXT,如果不规定结果不可预测 */
DCL VAR(&TEXT) TYPE(*CHAR) LEN(10)
CALLPRC PRC(PROC1) PARM('0')
CALLPRC PRC(PROC1) PARM('1' &TEXT)
CALLPRC PRC(PROC1) PARM('1''Goodbye')
ENDPGM
PROC1: PGM PARM(&P1 &P2) /* PROC1 – 带有可选参数&P2的过程 */
DCL VAR(&P1) TYPE(*LGL) /*标记指出是否规定了&P2,如果值为1,
即规定了&P2。 */
DCL VAR(&P2) TYPE(*CHAR) LEN(10)
DCL VAR(&MSG) TYPE(*CHAR) LEN(10)
DCL VAR(&PARMPOS) TYPE(*CHAR) LEN(4) /* CEEDOD的参数位置 */
DCL VAR(&PARMDESC) TYPE(*CHAR) LEN(4) /* CEEDOD的参数说明 */
DCL VAR(&PARMTYPE) TYPE(*CHAR) LEN(4) /* CEEDOD的参数数据类型 */
DCL VAR(&PARMINFO1) TYPE(*CHAR) LEN(4) /* CEEDOD的参数信息 */
DCL VAR(&PARMINFO2) TYPE(*CHAR) LEN(4) /* CEEDOD的参数信息 */
DCL VAR(&PARMLEN) TYPE(*CHAR) LEN(4) /* CEEDOD的参数长度 */
DCL VAR(&PARMLEND) TYPE(*DEC) LEN(3 0) /* 参数长度的十进制格式 */
IF COND(&P1) THEN(DO) /* 规定了Parm 2,用它作信息正文 */
CHGVAR VAR(%BIN(&PARMPOS 1 4)) VALUE(2) /* 告诉CEEDOD对第二个参
数要操作主字码 */
CALLPRC PRC(CEEDOD) PARM(&PARMPOS & PARMDESC +
&PARMTYPE &PARMINFO1 &PARMINFO2 &PARMLEN) +
/* 调用CEEDOD得到&P2的数据长度 */
CHGVAR VAR(&PARMLEND) VALUE(%BIN(&PARMLEND 1 4)) /* 把由CEEDOD
返回的长度转换成十进制格式 */
CHGVAR VAR(&MSG) VALUE(%SST(&P2 1 &PARMLEND)) /* 复制传送到本
地变量的数据 */
ENDO
ELSE CMD(CHGVAR VAR(%MSG) VALUE('Hello')) /* 用"Hello" 作信息正文*/
SNDPGMMSG MSG(&MSG)
ENDPGM
3.4.1 使用CALL命令
在CL过程中发布CALL命令时,传给被调用程序的参数值可以是字符串常量、数值常量、逻辑常量或CL变量,最多可传送40个参数。参数的值是按在CALL命令中的顺序传送的,传送的变量名不必须与接收参数的名字相同,接收值的变量名必须在被调用程序中说明,但说明的顺序不重要。
被调用程序和它接收的变量在存储位置之间没有联系。当传送变量时,变量存储在最初说明它的程序中,变量是由地址传送的。当传送常量时,常量的复本出现在调用程序中,复本的地址传送给被调用程序。
变量传给被调用程序时,它可以被修改,且修改影响到调用程序,新值不必须返回给调用程序以后用,即不需要特别编码把变量的值返回给调用程序。当传送常量时,它的值可由被调用用程序修改,也不用通知调用程序。这样,如果调用程序再重新调用同一程序,它要再初始化成常量值而不是变量的值。
使用CALL调用一个还没有编译的CL程序时(交互的CALL或用SBMJOB),*DEC类型的参数用LEN(15 5),*CHAR用LEN(32)传给接收程序。
不在CL过程或程序中的CALL命令不能传送变量。当CALL运行一个定义为*CMDSTR的命令参数时,在PARM中定义的任何变量的内容都转换成常量。在SBMJOB、ADDJOBSCDE和CHGJOBSCDE命令中的CMD参数便是例子。
在传递和接收参数时要注意下列情况:
长度等于或小于32字节的字符常量可用32字节长传送。(在右边加空格)。如果
它比32字节长,则传送整个长度,如果定义的参数长度大于32字节,CALL命
令必须传送确切长度的常量。比32长的常量不添加空格来匹配接收程序规定的长度。
接收程序可以接收传过来参数的一部分。例如,如果程序规定要接收4个字符,而传过来的是ABCDEF(其余26位空格),则仅接收ABCD给程序用。
如果接收程序接收比传过来多的字节数,则结果不可预测。做为字符值传送的数字值必须用引号括起。
系统用*CMDSTR计算参数中的字符。因括号内的第一个字符做为计数的第一个字符,命令名、参数名、空格、变量长度、引号都做为字符计数。如果在命令串中使用字符变量,要加一个替代值且计算引号。
十进常量用压缩格式传送且长度为(15 5),这样,如果传关12345,接收程序必须用LEN(15 5)说明一个十进字段,参数以12345.00000接收。
如果传送数值常量而接收程序不想以(15 5)的长度接收,则常量要转换成十六进制格式。下列命令是要把25.5传给程序变量,说明为LEN(5 2)
CALL PGMA PARM(X'02550F')
逻辑常量用32个字节的长度传送,逻辑值0或1放在第一个字节,其余字节为空
格,如果非0,1值传给用逻辑值的程序,结果是不可预测的。
浮点整数或浮点特殊值(*NAN、*INF或*NEGINF)做双精度值传送,它含8个
字节,虽然CL程序不能处理浮点数,但它能接收浮点值放在字符变量中,且把
变量传给能处理浮点值的HLL程序。
从CL程序或过程中的CALL也能传送变量,此时接收程序必须说明这个字段使
之与调用程序或过程中定义的变量相匹配。例如,如果CL过程或程序定义了一
个LEN(5 0)的变量&CHKUM,接收程序必须说明字段为压缩的无小数位的5位
数字。在CL过程或程序中用SBMJOB使CALL在批方式下运行时,变量做为常
量来对待。
如果十进常量或程序变量能传给被调用程序,参数必须定义为LEN(15 5)。任何调用程序须符合定义,如果类型、数量、顺序和长度在调用和接收程序之间不匹配结果不可预测。
由于空值不能传给另外的程序。反以不能用*N规定空值。
在下例中,程序A传送六个参数:一个逻辑值、三个变量、一个字符常量和一个数值常量。
PGM /* PROGRAM A */
DCL VAR(&B) TYPE(*CHAR)
DCL VAR(&C) TYPE(*DEC) LEN(15 5) VALUE(13.529)
DCL VAR(&D) TYPE(*CHAR) VALUE('1234.56')
CHGVAR VAR(&B) VALUE(ABCDEF)
CALL PGM(B) PARM('1' &B &C &D XYZ 2) /* 注意参数间的空格 */
.
.
.
ENDPGM
PGM PARM(&A &B &C &W &V &U) /* PROGRAM B */
DCL VAR(&A) TYPE(*LGL)
DCL VAR(&B) TYPE(*CHAR) LEN(4)
DCL VAR(&C) TYPE(*DEC)
/* 缺省长度 (15 5) 适合程序A的 DCL LEN */
DCL VAR(&W) TYPE(*CHAR)
DCL VAR(&V) TYPE(*CHAR)
DCL VAR(&U) TYPE(*DEC)
.
.
. ENDPGM
注:如果传给PGMB的第五个参数是456而不是XYZ且扩充为字母数字数据,
则参数的值为456。
逻辑常量‘1’不非得在调用程序中说明,它在程序B中的名为&A是逻辑类型。由于在&B的DCL语句中没有规定长度,所以传送缺省长度32个字符,仅规定了&B的六个字符(ABCDEF),由于在程序B中&B说明为四个字符,那么仅接收四个字符。如果在程序B中修改了它们,那么在程序A中的&B的四位也要修改。
在程序A中必须规定&C的LEN参数,如果没规定,用缺省长度,它将与程序B中想要的缺省长度不兼容,&C的值为13.52900。
程序B中的&W(程序A的&D)由于说明是字符型,故做为字符接收。如果类型为*CHAR,则不用引号。在程序A中,长度为缺省值7(小数点也考虑做一位),程序B希望长度为32,传送头7个字符,而7位后的内容不能断定。
变量&V是字符串XYZ,在左边添空格。变量&U是数值数据2.00000。
3.4.2 在调用过程或程序中的一般错误
下面介绍用CALL或CALLPRC命令传送值时比较常见的错误。其中某些错误很难调试,某些对程序功能产生严重后果。
3.4.2.1 使用CALL的数据类型错
命令串的总长度包括命令名、空格、参数名、括号、变量的内容及引号,对大多数命令,命令初始为处理程序期望的那样,但某些命令或变量不象预期的那样传送。
当在SBMJOB中与CMD参数一起用CALL时,可能发生非期望情况。按语法规则,当使用CMD参数和直接编译CALL命令都可出现CALL语句,在用CMD参数时,CALL命令转换成命令串,批处理子系统初始化它以便后来运行。当CALL用于自身时,CL编译程序生成编码来完成调用。
一般经常发生问题的是十进常量和字符串变量,在下列情况下不需要构成命令串:
十进数转换成十进常量。
在运行命令串时,十进常量用LEN(15 5)压缩格式传送,它不以在CL变量中规
定的格式传送。
字符变量说明为长度大于32字符。
字符变量的内容如前所述传送。通常做为去掉尾部空格的字符常量放在引号内。这样,被调用程序可能得不到足够的数据。
下面的方法可以改正构成命令串时发生的错误:
生成一个用于提交的CALL命令串,它把命令的变量部分连在一起形成一个CL
变量,用SBMJOB中的RQSDTA参数提交这个命令。
对于长度大于32个字符且结尾空白有意义的CL字符变量,生成一个的需长度的
变量,以非空的字符结尾,这就避免了截断有意义的空白。被调用程序由于依据
了预期的长度而忽略额外的字符。
生成一个初始化被调用程序的命令。提交这个新命令代替CALL命令,命令定义保证了参数用处理程序期望的那样传送给命令。
3.4.2.2 数据类型错
当传送一个值时,调用过程或程序与被调用过程或程序的数据类型(TYPE参数)必须相同(*CHAR,*DEC或*LGL)。当试图传送数值常量时经常发生类型错误。如果数字常量放在引号中,它是做为字符串传送的,如果不放在引号中,它是按LEN(15 5)的压缩数值字段传送的。
在下例中,引号中的数字值传给希望是十进值的程序,在被调用程序(PGMA)引用&A时发生十进错误(MCH1202)
CALL PGMA PARM('123') /* CALLING PROGRAM */
PGM PARM(&A) /* PGMA */
DCL &A *DEC LEN(15 5) /* DEFAULT LENGTH */
.
.
.
IF (&A *GT 0) THEN(...) /* MCH1202 OCCURS HERE */
在下例中,一个十进值传送给定义了字符变量的程序。一般说,这个错误不导致运行失败,但通常是结果不正确:
CALL PGMB PARM(12345678) /* CALLING PROG */
PGM PARM(&A) /* PGMB */
DCL &A *CHAR 8
.
.
.
ENDPGM
PGMB中的&A有十六进制值001234567800000F。
一般讲,数据可以从逻辑变量传送给字符变量,反之也可,不出错,值为‘0’或‘1’。
3.4.2.3 十进长度和精度错
如果一个十进制用不正确的十进长度和精度(太长或太短)来传送,在引用变量时会发生十进数据错(MCH1202)。在下例中,数值常量以LEN(15 5)传送,而在被调用程序中说明为LEN(5 2)。数值常量总是用压缩十进制(15 5)传送:
CALL PGMA PARM(123) /* CALLING PROG */
PGM PARM(&A) /* PGMA */
DCL &A *DEC (5 2)
.
.
.
IF (&A *GT 0) THEN(...) /* MCH1202 OCCURS HERE */
如果一个十进变量在调用过程或程序中说明为LEN(5 2),传送时做为变量而不是常量,则不发生错误。
如果要传送一个数值常量给过程或程序,而它希望长度不是(15 5),常量可用十六进制编码。下列命令给出如何把值25.5传给说明为(5 2)的程序变量:
CALL PGMA PARM(X‘02550F’)
如果一个十进值用正确长度但错误的精度(小数位)传送,接收的过程或程序就不能正确解释。在下例中,数值常量值(15 5)传给程序时处理为25124.00。
CALL PGMA PARM(25.124) /* CALLING PGM */
PGM PARM(&A) /* PGMA */
DCL &A *DEC (15 2) /* LEN SHOULD BE 15 5*/
.
.
.
ENDPGM
这个错误发生在第一次引用变量时,而不是传送和说明变量时。在下例中,被调用程序不引用变量,而是简单的放一个值在变量中返给调用程序,在变量返给调用程序后第一次引用时才发生错误,这类错误很难发现。
PGM /* PGMA */
DCL &A *DEC (7 2)
CALL PGMB PARM(&A) /* (7 2) 传送给程序B */
IF (&A *NE 0) THEN(...) /* 这里发生 *MCH1202 */
.
.
ENDPGM
PGM PARM(&A) /* PGMB */
DCL &A *DEC (5 2) /* 错误长度 */
.
.
.
CHGVAR &A (&B-&C) /* 把值放在 &A 中*/
RETURN
当控制返给PGMA,第一次引用&A时出错。
3.4.2.4 字符长度错
如果传送的字符值比接收变量的字符长,接收的过程或程序不能访问超出的长度。在下例中,PGMB把传给它的变量改为空格,由于变量说明为LEN(5),在PGMB中,仅有5个字符改成了空格,而PGMA引用时,其余部分字符不变。
PGM /* PGMA */
DCL &A *CHAR 10
CHGVAR &A 'ABCDEFGHIJ'
CALL PGMB PARM(&A) /* PASS to PGMB */
.
.
.
IF (&A *EQ ' ') THEN(...) /* 检查失败 */
ENDPGM
PGM PARM(&A) /* PGMB */
DCL &A *CHAR 5 /* 长度错误 */
CHGVAR &A ' ' /* 仅用5位;其他没有影响 */
RETURN
这类错误不引起逃逸信息,用这种方法处理的变量功能与期望值不同。
如果传送的值比要接收的值短,那么可能是更严重的后果。这时,被调用程序中变量的值由传送的原值组成,其余部分就不知道是什么内容。如果传送的值是变量,它可能由其它变量接上或由过程或程序的内部控制指令接上,如果传送的值是常量,它可能由CALL或CALLPRC传送的其它常量接上或由内部控制指令接上。
假如接收的过程或程序修改这个值,它在原值和所在存储上操作,这马上会影响修改其它变量或常量,或修改内部指令而引起过程或程序失败,修改所在存储会立即起作用。
在下例中,二个三字符常量传给被调用程序,用CALL命令传送最少32个字符。(通常,传送3个字符后用空格填充)如果接收程序说明接收变量比32长,那么用未知的所有存储填上额外的位,在此例中,假定二个常量在存储中是相邻的:
CALL PGMA ('ABC' 'DEF') /* PASSING PROG */
PGM PARM(&A &B) /* PGMA */
DCL &A *CHAR 50 /* VALUE:ABC+29' '+DEF+15' ' */
DCL &B *CHAR 10 /* VALUE:DEF+7' ' */
CHGVAR VAR(&A) (' ') /* THIS ALSO BLANKS &B */
.
.
.
ENDPGM
做为变量传送的值情况与此非常一样。
在下例中,二个三字符常量传给被调用程序。用CALLPRC命令仅传送规定的字符,如果接收程序说明的变量比传送的长,额外的位置用未知的所在存储填充:
在下例中,假定两个常量在存储中是相邻的:
CALLPRC PRCA ('ABC' 'DEF') /* PASSING PROG */
PGM PARM(&A &B) /* *PRCA */
DCL &A *CHAR 5 /* VALUE:'ABC' + 'DE' */
DCL &B *CHAR 3 /* VALUE:'DEF' */
CHGVAR &A ' ' /* &B的头两个字节为空格 */
.
.
.
ENDPGM
3.5 使用数据队列在程序和过程间通讯
数据队列是程序员生成的系统目标。HLL过程或程序可以往其中传送数据,另外的过程或程序可以从中接收数据,接收程序可以等待数据,也可在晚些时候接收数据。使用数据队列的优点是:
使用数据队列可以使作业更灵活的做一些工作。如果作业是交互作业,它可以提供更好的响应时间,减少交互程序的大小及它的处理访问组(PAG)。这样,就可帮助提高系统性能。例如,几个工作站用户都做修改和增加文件记录的操作,那么交互作业把请求都交给某个交易做为一个批作业执行,这样系统会完成的更好。
数据队列是两个作业之间异步通讯最快的方法,它比使用数据文件、信息队列发送和接收数据要更合算。
可在任何HLL过程或程序中调用QSNDDTAQ、QRCVDTAQ、QCLRDTA、QMHRDQM和QMHQRDQD程序来与数据队列传送数据和得到数据队列的描述,而不用结束HLL或调用其它CL过程或程序做这些事。
当从数据队列接收数据时,可以设置一个延迟时间,让作业在此时间内等着接收数据队列来的数据。这与OVRDBF命令中的EOFDLY参数是不同的,它在无论何时延迟时间结束,作业是活动的。
多个作业可从同一数据队列接收数据,这就使在一定的性能限制下应用程序可以处理多个作业。例如,在系统中有多个打印机可用,几个交互作业往一个数据队列发送请求,每个打印机作业可从数据队列接收数据,可用FIFO、LIFO或键字顺序来接收数据。
数据队列给每个放在其中的信息建一个发送ID。这个ID是在生成队列时建立起来的属性,其中有限定的作业名和用户配置文件信息。
下例给出数据队列如何工作,几个作业在同一数据队列都有入口,这些入口是由一个服务作业管理的,它使多个作业往一个打印作业中发送处理请求,多个作业可往同一队列发送数据:
DTAQ
下面是另一个使用数据队列的例子。作业得到工作请求把它送到数据队列中(调用QSNDDTAQ)。服务作业从数据队列接收请求(调用QRCVDTAQ)且处理数据。服务作业也能用另外的数据队列把状态报告给主作业。数据队列允许主作业把工作按规定路线发送给服务作业,这就让主作业可以接收下一个工作请求,可有多个服务作业从同一数据队列接收信息。
主作业 数据队列 服务作业
CALL QSNDDTAQ CALL QRCVDTAQ
工作 INPUT
作业1 作业2 作业3 |
作业1 作业2 作业3 |
STATUS
DTAQ
当数据队列中无信息时,服务作业有以下选项:
等待队列中有信息
等待一个时间周期,如仍无信息到达,则继续处理
不等待,立即返回
数据队列也能用于程序需要等待从数据队列与过程或程序通讯的显示文件、ICF文件和数据队列输入数据时,此时,要在下列命令中用DATQ参数:
CRTDSPF、CHGDSPF、OVRDSPF、CRTICCF、CHGICFF、OVRICFF
可以在下列情况下指出数据队列中已有信息:
从请求的显示设备上输入ENABLE命令或按执行键。
数据从请求的ICF对话中成为可用的。
用CRTOUTQ和CHGOUTQ命令可以使与数据队列有关的选项用于输出队列,在假脱机文件在输出队列中是RDY(准备好)状态时,会在数据队列中有一个入口记录,用户程序能确定什么时候有可用的SPLF,再从数据队列接收信息。
系统中运行的作业能用在QSNDDTAQ程序中规定DATQ参数来往同一数据队列中放信息,用程序调用QRCVDTAQ接收放在数据队列中的每项信息,然后根据是由显示文件、ICF文件或QSNDDTAQ发送的不同情况来进行处理。详细内容请看3.5.7.2和3.5.7.3。
3.5.1 远程数据队列
可用DDM文件访问远程数据队列,用DDM文件可使在一个AS/400的程序访问远程AS/400的数据队列来完成下列功能:
往数据队列发送数据
从数据队列接收数据
清除数据队列中的数据
当前使用标准数据队列的应用程序不用再修改或编译就能访问远程的DDM数据队列。要保证访问正确,可做以下之一:
删除标准的数据队列,生成同名的DDM数据队列
把标准的数据队列改名
可用下列命令生成DDM数据队列:
CRTDTAQ DTAQ(LOCALLIB/DDMDTAQ) TYPE(*DDM)
RMTDTAQ(REMOTELIB/REMOTEDTAQ) RMTLOCNAME(SYSTEMB)
TEXT('DDM data queue to access data queue on SYSTEMB')
也可用前面的例子(主作业/服务作业)来生成DDM数据队列来使用远程数据队列。主作业在系统A中,数据队列和服务作业放在系统B中。在生成两 个DDM数据队列后(INPUT和STATUS),主作业继续异步地与系统B中的服务作业通信。下例给出如何生成做为远程数据队列的DDM数据队列:
CRTDTAQ DTAQ(LOCALLIB/INPUT) TYPE(*DDM)
RMTDTAQ(REMOTELIB/INPUT) RMTLOCNAME(SystemB)
TEXT('DDM data queue to access INPUT on SYSTEMB')
CRTDTAQ DTAQ(LOCALLIB/STATUS) TYPE(*DDM)
RMTDTAQ(REMOTELIB/STATUS) RMTLOCNAME(SystemB)
TEXT('DDM data queue to access STATUS on SYSTEMB')
主作业调用QSNDDTAQ,然后传送数据队列名LOCALLIB/INPUT和数据到系统B的远程数据队列中(REMOTELIB/INPUT)。主作业调用QRCVDTAQ来传送数据队列名LOCALLIB/INPUT。
主作业 系统B
CALL QSNDDTAQ CALL QRCVDTAQ
工作 INPUT
作业1 作业2 作业3 |
作业1 作业2 作业3 |
STATUS
图3—1访问远程数据队列的例子
3.5.2 使用数据文件和数据队列的比较
下面说明使用数据队列与数据文件间的不同:
数据队列能在活动的过程或程序间提供通讯,不能存储大量的数据,如果要这样,用数据文件做队列。
数据队列不能用做数据的长项目存储,要做这用数据文件。
在使用数据队列时,在程序中要有非正常结束的例程,来在系统结束前恢复没处理完的项目。
最好是周期性的(比如一天一次)删除、再生成数据队列,如果队列中有太多内容不取消,则会影响系统性能。周期性的再生成数据队列能使数据队列恢复到满意的大小。
3.5.3 与信息队列的相似点
数据队列与信息队列很相似,这体现在传送和接收数据方面。但多个程序可同时从一个数据队列接收数据,而只有一个程序能从一个信息队列中接收数据。(仅一个程序从数据队列接收一项,其它多个程序要等待)。数据队列中的项是按FIFO、LIFO或键字顺序处理的,当接收一项时,它从数据队列中取消。
3.5.4 使用数据队列的准备工作
要使用数据队列,要先用CRTDTAQ命令生成,下面是命令的例子:
CRTDTAQ DTAQ(MYLIB/INPUT) MAXLEN(128)
TEXT('Sample data queue')
MAXLEN参数规定送到数据队列中项的总长度(1—64512字符)。
3.5.5 数据队列使用的存储管理
在从数据队列接收一项后,这项从数据队列取消但并没释放所占存储。在数据队列中有新的项送到时,会再使用同一存储空间,队列所占空间随着送到项的增加但没被接收而增大。如果数据队列的大小能保持在不多于100项时,性能会好,如果数据队列太大,要用DLTDTAQ命令删除,再用CRTDTAQ生成。
3.5.6 分配数据队列
如果应用程序需要一个不能同时让多个作业访问的数据队列,那么在使用数据队列之前要写一个有ALCOBJ命令的语句,在应用程序使用完它之后用DLCOBJ命令重新分配数据队列。
ALCOBJ命令本身不能限制另外的作业从数据队列中发送和接收数据及清除数据队列。但如果所有的应用程序在使用数据队列前都编码了包括ALCOBJ命令的语句,那么在数据队列已经分配给一个作业时如再分配给另一个就会失败,即在同一时刻要防止多个作业使用数据队列。此时,系统分发送CPF1002错误信息,可在应用程序中用MONMSG命令监控这个信息且响应它,可以给用户发送信息来重新分配数据队列。
3.5.7 使用数据队列的例子
下面的例子给出处理数据队列文件的三种方法。
3.5.7.1 例1.等待二小时从数据队列接收数据
下例中,程序B等待二小时从数据队列接收数据项,程序A往QGPL库中的DTAQ1数据队列发送数据项。如果程序A在二小时之内发送了一项,程序B接收它,立即处理,如果过了二小时程序A没发送,由于字段长度为零,故程序B处理超时条件。程序B继续接收数据项直到出现超时条件。此程序是用CL写的,但可用任何高级语言写。
用下列命令生成数据队列:
CRTDTAQ DTAQ(QGPL/DTAQ1) MAXLEN(80)
在此例中,所有数据队列项都是80字节长。
在程序A中,下列语句与数据队列有关:
PGM
DCL &FLDLEN *DEC LEN(5 0) VALUE(80)
DCL &FIELD *CHAR LEN(80)
.
.(determine data to be sent to the queue)
.
CALL QSNDDTAQ PARM(DTAQ1 QGPL &FLDLEN &FIELD)
.
.
.
在程序B中,下列语句与数据队列有关:
PGM
DCL &FLDLEN *DEC LEN(5 0) VALUE(80)
DCL &FIELD *CHAR LEN(80)
DCL &WAIT *DEC LEN(5 0) VALUE(7200) /* 2 小时 */
.
.
.
LOOP: CALL QRCVDTAQ PARM(DTAQ1 QGPL &FLDLEN &FIELD &WAIT)
IF (&FLDLEN *NE 0) DO /* 接收数据项 */
.
. (处理数据队列中的数据)
.
GOTO LOOP /* 从数据队列得到下一项 */
ENDDO
.
. (2小时没有接收到数据;处理超时条件)
.
3.5.7.2 例2.等待从显示文件和ICF文件的输入
下例中只有一个作业用数据队列。数据队列在作业中的各目标之间通讯而不仅是在两个作业之间通讯。
ICF文件 应用程序 显示文件 数据队列
在此例中,程序等待来自显示文件和ICF文件中的输入。
不用交替的一个接一个等待,而用数据队列使程序等待一个目标(数据队列)。程序调用QRCVDTAQ且等待显示文件和ICF文件中规定的放到数据队列中的项。当在这两个文件中有可用数据时,相应的数据管理把两类项放到数据队列中。ICF文件项用*ICFF开始,显示文件项用*DSPF开始,它们放在数据队列中是80个字符长,其中包括在下表中给出的属性说明,这样,用CRTDSPF、CHGDSPF、OVRDSPF、CRTICFF、CHGICFF、OVRICFF命令规定的数据队列必须至少有80个字符长。
位置 数据类型 说明
1-10 字符 放在数据队列中的文件类型,有两个:*DSPF和*ICFF
如果作业从队列接收的数据仅打开了一个显示文件或ICF文件,仅用这一个字段来决定从数据队列接收的项类型。
11-12 二进制 这是唯一的文件标识,这个标识与文件的打开反馈区
值是一样的。仅在多个文件以相同名字往数据队列中
放数据项时,程序用此字段接收数据队列中的项。
13-22 字符 显示文件或ICF文件名,这是在所有替换命令处理完
后实际打开的文件名,与在文件的打开反馈区的名字
相同。它仅用在有多个显示文件或ICF文件在数据队
列中有数据项时,程序用这个字段接收数据项。
23-32 字符 文件所在的库名。这是一个在所有替换操作执行完后
的库名,它与文件在打开反馈区中所用库名相同。这
个字段仅用在有多个显示文件和ICF文件在数据队列
中有数据项时,程序用它接收数据项。
33-42 字符 程序设备名,它与在打开反馈区中程序设备定义清单
中所用的名相同。对于*DSPF,这是有可用数据的程
序设备名,仅在有多个设备或对话往数据队列放数据
项时,程序才用它接收数据项。
43-80 字符 保留。
下面给出前面程序的编码逻辑:
.
.
.
.
OPEN DSPFILE ... /* 打开显示文件。 在文件的CRTDSPF, CHGDSPF, 或
OVRDSPF命令中规定 DTAQ 参数 */
OPEN ICFFILE ... /* 打开ICF文件。在文件的CRTICFF, CHGICFF, 或
OVRICFF命令中规定 DTAQ 参数 */
.
.
DO
WRITE DSPFILE /* 显示文件写请求 */
WRITE ICFFILE /* ICF文件写请求 */
CALL QRCVDTAQ /* 从文件的DTAQ参数规定的数据队列接收数据项。 */
/* 在请求设备上或在文件对话中有可用数据时,把数据 */
/* 项放在数据队列中。 */
/* 接收数据项后,确定哪个文件有可用数据,读数据处 */
/* 理它,再请求文件,返回到处理数据队列的下一项。 */
IF 'ENTRY TYPE' FIELD = '*DSPF ' THEN /* 来自显示文件的数据项,*/
DO /* 此项不包括接收的数据,*/
/* 那么要先从文件中读数据 */
/* 然后处理。 */
READ DATA FROM DISPLAY FILE
PROCESS INPUT DATA FROM DISPLAY FILE
WRITE TO DISPLAY FILE /* 写请求 */
END
ELSE /* 来自ICF文件的数据项, */
/* 此项不包括接收的数据,*/
/* 那么要先从文件中读数据 */
/* 然后处理。 */
READ DATA FROM ICF FILE
PROCESS INPUT DATA FROM ICF FILE
WRITE TO ICF FILE /* 写请求 */
LOOP BACK TO RECEIVE ENTRY FROM DATA QUEUE
.
.
.
END
3.5.7.3 例3.等待来自显示文件和数据队列的输入
在下例中,作业B中的程序等待从作业A中往数据队列中放数据项。它是用显示文件送给作业B中的程序。替代的方法是程序仅从数据队列一个目标中得到输入。
作业A
批作业,把用户项放到数据队列中 数据队列 交互作业,用显示文件及从数据队列中接收数据项
显示文件
程序调用RCVDTAQ,等待数据项放到显示文件规定的数据队列中,作业A也把项放在同一数据队列中。有两类项放到这个队列中:显示文件项和用户定义的项。显示文件项是在显示文件中有可用数据时由显示数据管理放到数据队列中的项,用户定义的项是由作业A放到数据队列中的。
显示文件项的结构在前例中已说明,用户定义项的结构是由应用程序员定义的。
下例给出作业B中应用程序的编码逻辑:
.
.
.
OPEN DSPFILE ... /* 打开显示文件。在文件的CRTDSPF, CHGDSPF, or OVRDSPF*/
/* 命令中规定 DTAQ 参数 */
.
.
DO
WRITE DSPFILE /* 写显示文件请求 */
CALL QRCVDTAQ /* 从DTAQ规定的数据队列接收数据项,在显示文件请求的 */
/* 设备上有可用数据时,由作业A或显示数据管理把数据 */
/* 项放在数据队列中。 */
/* 在接收数据项后,确定它的类型,处理它,返回到接收 */
/* 数据队列的下一项。 */
IF 'ENTRY TYPE' FIELD = '*DSPF ' THEN /* 来自显示文件的数据项。*/
DO /* 此项不包括接收的数据,*/
/* 那么要先从文件中读数据 */
/* 然后处理。 */
READ DATA FROM DISPLAY FILE
PROCESS INPUT DATA FROM DISPLAY FILE
WRITE TO DISPLAY FILE /* 写请求 */
END
ELSE /* 来自作业A的数据项。 */
/* 它包括从作业A来的数据 */
/* 所以在处理前不用读数据*/
PROCESS DATA QUEUE ENTRY FROM JOB A
LOOP BACK TO RECEIVE ENTRY FROM DATA QUEUE
.
.
.
END
3.6 使用数据区在过程和程序间通讯
数据区是用来放数据以便在系统中运行的作业访问的一类目标。在需要存储有限大小的信息时用数据区。它与过程或文件是相独立的。数据区的典型用法是:
提供一个很容易很灵活修改的字段来控制作业中的引用。例如:
-提供分配的下一个顺序号。
-提供下一个检测数。
-提供下一个供保存/重存用的中间卷。
提供一个区来传送作业内的信息,这个区可在每个作业的QTEMP库中。
提供一个常量字段给几个作业用,例如税率和分布清单。
给需要数据区的较大处理程序提供一个有限的访问方式。
一个数据区可以由一个用户锁住。这样就避免其它用户同时操作。
要生成一个非本地或成组数据区,用CRTDTAARA命令,它可以在指定的库中生成有初值的数据区。要在CL过程或程序中使用这个值,用RTVDTAARA命令,它把数据区的当前值送给过程或程序中的变量。如果在CL过程或程序中修改此值且想把新值返回给数据区,用CHGDTAARA命令。要显示数据区中的当前值,用DSPDTAARA命令,可用DLTDTAARA命令删除数据区。
3.6.1 本地数据区
本地数据区是由系统中的每个作业建立的。包括自动启动的作业,由读出器启动的作业以及子系统监控的作业,系统来生成本地数据区且初值为全空格,长度为1024,类型为*CHAR。当用户用SBMJOB命令提交作业时,提交作业的数据区值复制到被提交作业的数据区中,可用CHGDTAARA、RTVDTAARA、DSPDTAARA中DTAARA(*LDA)或对*LDA用子串功能(%SST)来引用自己作业的本地数据区。
在使用本地数据区要注意:
不能从一个作业引用另一作业的本地数据区。
你不能生成、删除或分配本地数据区。
本地数据区不与库有关。
本地数据区的内容存在例程步边界之上。因此TFRJOB、TFRBCHJOB、RRTJOB、RETURN命令不会影响本地数据区的内容。
可用本地数据区做下列事情:
不用参数列表给过程或程序传送信息
把信息装入到酝数据区提交作业,把信息传给被提交的作业的作业,然后从被提
交作业中访问数据
比用其它类型数据区访问过程或程序的性能要好
不用自己生成和删除数据区来存储信息
多数高级语言都能使用本地数据区。SBMxxxJOB和STRxxxRDR命令启动一个本地数据区且把它初始化为空格。仅SBMJOB命令可以把提交作业的本地数据内容传送给新作业。
3.6.2 成组数据区
在交互作业成为组作业时(用CHGGRPA命令),系统生成成组数据区。对这个组仅可存在一个成组数据区,在组中最后一个作业结束时(用ENDJOB、SIGNOFF或ENDGRPJOB命令或异常结束)删除成组数据区,在作业不再是成组作业的一部分时,(在CHGGRPA中规定GRPJOB(*NONE))也删除成组数据区。
成组数据区由空格初始化,长度为512,类型为*CHAR。组中作业可用CHGDTAARA、RTVDTAARA、DSPDTAARA命令的参数DTAARA(*GDA)来使用成组数据区,此数据区可为组中的所有作业使用。
在使用成组数据区时,要注意:
不能用成组数据区做字符变量的子串,但可用子串功能把512字节字符移进或移
出成组数据区。
成组数据区不能被组外作业引用。
成组数据区与库无关。
成组数据区的内容不能用TFRGRPJOB命令修改。
用成组数据区可在同组中各作业之间通讯。例如,在使用CHGGRPA命令后,可用下列命令设置成组数据区的值:
CHGDTAARA DTAARA(*GDA) VALUE('january1988')
这个命令可从一个程序中运行也可由工作站用户使用。组中的任何其它CL过程或程序可以用下列命令取得数据区的值:
RTVDTAARA DTAARA(*GDA) RTNVAR(&GRPARA)
它把成组数据区的值(JANUARY1988)放到变量&GRPARA中。
3.6.3 程序初始参数数据区(PIP)
PIP是由每个预先启动作业在开始时建立的。它的属性PDA与一般的数据区不同,它仅有由规定*PDA来引用。PDA是2000字节长但不限制在其中的参数个数。
RTVDTAARA、CHGDTAARA、DSPDTAARA命令及RTVDTAARA和CHGDTAARA宏指令支持数据区名字参数中规定*PDA。
3.6.4 远程数据区
可用DDM来访问远程数据区。在一个AS/400上的应用程序要修改或取得在远程AS/400的数据区,不用修改和重新编译应用程序。要保证访问到正确的数据,做下列之一:
删除标准数据区,再生成一个与其同名的DDM数据区
把标准数据区改名
可用下列命令生成DDM数据区:
CRTDTAARA DTAARA(LOCALLIB/DDMDTAARA) TYPE(*DDM)
RMTDTAARA(REMOTELIB/RMTDTAARA) RMTLOCNAME(SYSTEMB)
TEXT('DDM data area to access data area on SYSTEMB')
要在CL程序中使用远程AS/400的数据区,用RTVDTAARA命令,规定DDM数据区的名字把其当前值放到程序变量中。如果修改值后要把它放回到远程数据区,用CHGDTAARA命令,在其中规定用一个DDM数据区。
如果在用DSPDTAARA时规定DDM数据区的名字,就显示DDM数据区中的值,而不是远程数据区的值,可用DLTDTAARA命令删除DDM数据区。
3.6.5 生成数据区
数据区要先生成然后才能使用。一个数据区可以生成为:
最长为2000字符的字符串。
根据数据区是仅用在CL过程或程序或也用在其它高级语言过程或程序,使用不
同属性的十进制值。对CL过程或程序,最多15位整数9位小数,但总共不能多
于15位。对其它语言,最多15位整数9位小数,但总共不能多于24位。
逻辑值‘0’或‘1’。
在生成数据区时,可规定初值。如果没规定,则用下列值:
十进制用零,字符用空格,逻辑值用‘0’。
要生成数据区,用CRTDTAARA命令,如下例:
CRTDTAARA DTAARA(CUST) TYPE(*DEC) +
LEN(5 0) TEXT('Next customer number')
3.6.6 数据区加锁和分配
CHGDTAARA命令中用*SHRUPD,在命令执行时会锁住数据区。
RTVDTAARA命令和DSPDTAARA用*SHRRD,在命令执行时会锁住数据区。
如果在数据区完成多个操作,要用ALCOBJ命令来避免其它用户在操作没完成时访问数据区。例如,同时有作业要读和增加数据区中的值,那么就要用ALCOBJ命令来保护读和修改的值。详细内容请看第四章。
3.6.7 显示一个数据区
可用DSPDTAARA命令显示一个数据区的属性(名字、库名、类型、长度、说明)以及值,显示使用24个数字格式,可存前置零。
3.6.8 修改一个数据区
可用CHGDTAARA命令修改一个数据区的全部或部分值,但不能修改其它属性。新值可以是常量或CL变量。如果命令是在CL过程中,则程序生成时数据区不一定要存在。
3.6.9 取得一个数据区
RTVDTAARA命令取得数据区的部分或全部内容,把它复制到一个变量中去。在编译时数据区不一定要存在,CL变量也不要求与数据区同名,它只是取得数据区的内容但不能修改它。
获得数据区的例子
例1.假定用名为ORDINFO的数据区来跟踪一个文件的状态,数据区的设计为:
位置1为O(打开),P(处理),或C(完成)
位置2为I(有存货),O(无存货)
位置3为定货单的初值
可如下说明字段:
DCL VAR(&ORDSTAT) TYPE(*CHAR) LEN(1)
DCL VAR(&STOCKC) TYPE(*CHAR) LEN(1)
DCL VAR(&CLERK) TYPE(*CHAR) LEN(3)
要把定货状态放在&ORDSTA中,写如下命令:
RTVDTAARA DTAARA(ORDINFO (1 1)) RTNVAR(&ORDSTAT)
要取得库存条件放到&STOCK中,写如下命令:
RTVDTAARA DTAARA(ORDINFO (2 1)) RTNVAR(&STOCKC)
要取得定货单的初始值放到&CLERK中,写如下命令:
RTVDTAARA DTAARA(ORDINFO (3 3)) RTNVAR(&CLERK)
每个RTVDTAARA命令都要访问数据区。要取多个子字段,不如取整个数据区放到一个变量中,然后用子串功能获得各子字段。
例2.下例把5个字符的数据区放到3个字符的变量中,做法如下:
生成名为DA1,5个字符的数据区(放在MYLIB库中)。初值为‘ABCDE’
说明名为&CLVAR1,长为3个字符的变量
把DA1的后三个字符复制到&CLVAR1中
所用命令如下:
CRTDTAARA DTAARA(MYLIB/DA1) TYPE(*CHAR) LEN(5) VALUE(ABCDE)
.
.
.
DCL VAR(&CLVAR1) TYPE(*CHAR) LEN(3)
RTVDTAARA DTAARA(MYLIB/DA1 (3 3)) RTNVAR(&CLVAR1)
现在&CLVAR1中有‘CDE’。
例3.下例把5位数字的数据区内容放到5位数字的变量中。
做法如下:
生成名为DA2(放在MYLIB中)的5位数字,两位小数,初值为12.39的数据区
说明名为&CLVAR2长为5位数字1位小数的变量
把DA2的内容复制到&CLVAR2中
命令如下:
CRTDTAARA DTAARA(MYLIB/DA2) TYPE(*DEC) LEN(5 2) VALUE(12.39)
.
.
.
DCL VAR(&CLVAR2) TYPE(*DEC) LEN(5 1)
RTVDTAARA DTAARA(MYLIB/DA2) RTNVAR(&CLVAR2)
现在&CLVAR2中内容为0012.3。
3.6.11 修改和取得数据区的例子
下例是用CHGDTAARA和RTVDTAARA命令做字符子串操作的例子。做法如下:
生成一个名为DA1(放在MYLIB中),10字符长,初值为ABCD5678IJ的数据区
说明名为&CLVAR1,5字符长的变量
把DA1中从第5位开始,5位长的字符放到&CLVAR1中
命令如下:
DCL VAR(&CLVAR1) TYPE(*CHAR) LEN(5)
.
CRTDTAARA DTAARA(MYLIB/DA1) TYPE(*CHAR) LEN(10) +
VALUE('ABCD5678IJ')
.
.
.
CHGDTAARA DTAARA((MYLIB/DA1) (5 4)) VALUE('EFG')
RTVDTAARA DTAARA((MYLIB/DA1) (5 5)) RTNVAR(&CLVAR1)
现在&CLVAR1的内容为‘EFG I’。