1、 Service program一 基本概念:Service program 是由 module 和其他 Service program 组成的,在系统中用*SRVPGM 来表示(好像是废话- -!) ,其包含了很多可被其他程序调用的 procedure。Service program 是用 by reference 方式绑定,功能类似于其他语言的函数库,提供了很多procedure 供其他 ILE program 调用。Service program 中哪些 procedure 可以被其他 program 调用,哪些不可以呢?这就牵扯到了Service program 中一个非常重要的概念:
2、public interface。二 公共接口(public interface)Service program 的 public interface 规定了哪些 procedure 和 data item 可以被外界使用,哪些不可。如果 public interface 设置的不合理,很容易产生 program 与 Service program 不兼容的问题,导致程序发生异常。那么如何定义 Service program 的 public interface 呢,如下:CRTSRVPGM 命令的 EXPROT 参数规定了 Service program 如何对外提供接口,并提供 2 个参数
3、值:1 EXPORT (*ALL)。选择*all 的话,那么 Service program 里所有使用 key word:EXPORT 修饰的 procedure 和 data item 都可以被外界程序使用。2 EXPROT(*SRCFILE)。*SRCFILE 是默认的参数值。使用*SRCFILE 的话,可以使用 binder language(BND 文件)来自定义 public interface,自己选择哪些 procedure 和 data item 可以被外界使用(这些 procedure、data item 也必须使用 key word:EXPROT 修饰) 。注:binde
4、r language 的文件类型是 BND,默认的 SOURCE FILE 是 QSRVSRC,并且 BND 文件是不需要编译的。下面说一下*ALL 和*SRCFILE 各自的优缺点:*ALL 的优点:使用起来非常方便,不需要额外的辅助文件,Service program 中所有的 export procedure 和 data item 就都可以被其他程序调用。*ALL 的缺点:兼容性太差,无论增加或者减少 Service program 的 export procedure、data item,都会导致 program 与 Service program 的不兼容。那么所有用到此 Serv
5、ice program 的program 都需要重新绑定(UPDPGM)或者重新编译(CRTPGM)才能正常使用。就算是这种变化并不会影响程序的正常运行(比如说你向 Service program 中追加了一个新的 procedure,并且现程序并不使用此 procedure,即增加 procedure 不影响现在的程序运行)那么所有用到此 Service program的 program 也要重新绑定或编译。很麻烦吧。*SRCFILE 的缺点:*SRCFILE 使用起来要比*ALL 麻烦些,因为要使用 binder language 定义BND 文件才能使用。*SRCFILE 的优点:提供了
6、很好的兼容性。即增加或者减少 Service program 的 export 的procedure、data item,程序仍然运行,不需要重新编译相关的 program。那到底是什么原因产生的兼容性问题?听我慢慢道来三 signature 与 binder language1 signature首先系统为 Service program 提供了一个类似 PF 的 level check 的功能,来检查 ILE program与 Service program 当前的接口是否一致。大概的过程如下:1)CRTSRVPGM 时,系统会根据 Service program 的 export pro
7、cedure、data item 的数量、名称、顺序产生一个 signature(类型于 PF 的 record format ID,具有唯一性) ,并保存在*SRVPGM中。例如: DSPSRVPGM SRVPGM(PASS) DETAIL(*SIGNATURE) splay Service Program Information Display 1 of 1 Service program . . . . . . . . . . . . : PASS Library . . . . . . . . . . . . . . . : MYLIB Owner . . . . . . . . .
8、. . . . . . . . : MYLIB Service program attribute . . . . . . . : RPGLE Detail . . . . . . . . . . . . . . . . . : *SIGNATURE Signatures: 0000000000000000C5F03F1A5845D322 2)CRTPGM 时,会将此 Service program 绑定到*PGM 中,同时会把 Service program 当前的 signature 值也保存在*PGM 中。例如:DSPPGM PGM(PASSBY2) DETAIL(*SRVPGM)Dis
9、play Program InformationService Opt Program Library Signature PASS *LIBL 0000000000000000C5F03F1A5845D322 QRNXIE QSYS D8D9D5E7C9C540404040404040404040 QRNXUTIL QSYS D8D9D5E7E4E3C9D34040404040404040 QLEAWI QSYS 44F70FABA08585397BDF0CF195F82EC1 3)当*PGM 被调用的时候,PGM 会激活 Service program(即把 Service program
10、 加入到active group 中) ,在激活的过程中会比较*PGM 中的 signature 值与*SRVPGM 的 signature 值是否一致。如果一致,则程序正常运行;如果不一致,则程序出现异常。大概就这么个过程。2 与 PF LEVEL CHECK(*YES/*NO)类似,你也可以指定是否进行 signature 的检查1)*ALL 的话,默认 LVLCHK(*YES)并且不能更改,会进行检查。那么当 Service program的 export procedure、data item 发生变化时(增加、减少) ,相应的 signature 值也发生变化了,如果不重新绑定,重新
11、编译相关的 program,那么就导致了 program 与 Service program 的signature 值不一样,必然导致不兼容。2)*SRCFILE 的话,可以选择 LVLCHK *YES 还是*NO。选择*NO 的话,就不用细说了,因为*NO 不会进行 signature 检查无论你怎么变化 procedure、data item 都不要紧。因为系统根本不会对 program 与 Service program 的 signature 进行对比,自然不存在兼容不兼容的问题。 (使用*NO要谨慎,没有系统的检查,可能会出现一下不可预知的错误)选择*YES 的话,你可以在一个 BN
12、D 文件中,定义多个 signature,只要*PGM 中保存的 signature 值与 BND 文件中任意一个 signature 值相当,程序就可以正常运行。举个例子吧:PGM1 用到了 SRVPGM1SRVPGM1 有 2 个 export procedure:export P1 export P2 BND 文件:STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) EXPORT SYMBOL(P1) EXPORT SYMBOL(P2) ENDPGMEXP假设经过编译后,PGM1 可以正常调用 SRVPGM1。现在 SRVPGM1 增加了一个 export p
13、rocedure,那么为了保持 PGM1 与 SRVPGM1 的兼容,我会增加一个 signature,如下:BND 文件:STRPGMEXP PGMLVL(*CURRENT) LVLCHK(*YES) EXPORT SYMBOL(P1) EXPORT SYMBOL(P2)EXPORT SYMBOL(P3) ENDPGMEXPSTRPGMEXP PGMLVL(*PRV) LVLCHK(*YES) EXPORT SYMBOL(P1) EXPORT SYMBOL(P2) ENDPGMEXP这样的话,即使我不重新编译 PGM1,PGM1 也可以正常调用 SRVPGM1(不过不能使用新追加的 P3,只
14、能使用*PRV 的 export block)注意:signature 的值是由 export procedure、data item 的名称、数量、顺序决定的,且具有唯一性。为了避免不兼容问题的发生,注意以下几点:1)不要破坏原来 export block 中 export procedure、data item 的顺序和数量。2)新追加 export block(即定义多个 signature)的话,新的 export block 必须要包含原来的 export block 中的内容,并且顺序、数量都不能改变。新追加的 export procedure、data item 要放 expor
15、t list 的最后面。(如上面例子所示)3)不要删除原 export block 中的 procedure、data item此外,还可以通过自定义 signature 的值,也可以解决兼容性问题,这里就不多说了。四 service program 的激活Service program 不能够直接激活。调用 SRVPGM 的 ILE PROGRAM 被调用的话,那么此SRVPGM 就自动被激活。Service program 的激活过程中,主要干 2 件事:1 通过符号链接与物理地址的转换,完成绑定工作。2 进行 signature checking。这两步之前都进行过详细说明,不再叙述。-
16、补充说明五 service program 的用途、目的、优势为什么要使用 service program 呢?service program 能给我们提供什么好处呢?我们之所以选择 service program,主要从以下 2 方面进行考虑:1 采用 service program,能使系统维护更加方便,提高可重用性(我认为是最主要的原因)2 采用 service program,可以节省硬盘空间,节省内存空间。下面还是摆事实、讲道理吧1) 可维护性:假如说有 N 个程序,都会调用 MOD1,不使用 service program 方式的话,就得使用 by copy 的方式绑定,即:CRT
17、PGM (PGM1) MODULE(MOD1)。那么,如果 MOD1 发生变更的话,这 N 个程序都需要重新绑定(UPDPGM)或者编译(CRTPGM),才能使用更新后的 MOD1。如果使用 service program 的话,把 MOD1 加入到 SRVPGM1 中。当 MOD1 再次发生变更时,如果 MOD1 的变更不影响 SRVPGM1 的 public interface(即 PGM1 与 SRVPGM1 仍然兼容) ,那么你只需要 UPDSRVPGM 即可,其他相关联的 N 个程序都不需要重新绑定或编译;如果 MOD1 的变更影响到了SRVPGM1 的 public interfa
18、ce 的话(比如说 MOD1 新增加了一个 export procedure,即 PGM1 与SRVPGM1 不兼容) ,那么你 UPDSRVPGM 之后,也只需要 UPDPGM(或 CRTPGM)与新追加的 export procedure 相关的 PGM 即可,不需要 UPDPGM 所有的 PGM,方便吧。2)节省空间:使用 by copy 的方式绑定,N 个程序中就有 N 个 MOD1 的代码。如果使用 service program,那么整个硬盘中实际上只有一个 MOD1 的代码,节省了硬盘空间。而在实际运行中,所有的用户实际上是共用一份 service program 的代码,系统会
19、给每个用户分配独立的静态变量区,但是代码是共用一份的。而使用 by copy 的方式,每个程序中,还是会包含一分 MOD1 的代码,这样 service program 也就节省了内存空间。六 实际使用情况如果一个 procedure 被多个程序调用的话,那么就应该把它加入到 service program 中。如果这个 procedure 仅仅是一次性使用的话,那么就没必要了。我认为理想的情况是,ILE *PGM 应该由一个 main procedure/module(提供 PEP)+ 其它service program 组成。具体的应用情况是,可以把功能类似的程序组成一个 service
20、program。比如把各种登录验证的程序,归为一个 service program;各种日期转换,日期编辑的程序放到一个 service program;还有各种处理代码程序,放到一个 service program 等。使用 service program 主要的一个麻烦需要注意 如何避免 Program signature violation影响 signature 的东西主要是 Exports,包括名称,顺序等,如果 exports 任何一个名称或顺序有明变化,则使用到该 service program 的所有程序都需要重新编译一遍,否则就会发生 program signatue vio
21、nation.一个 service program 主要是由可运行的 procedure 和 data items 组成相应地,在 service program 里有两类 exports, 一类是 procedures 的 export, 一类是 data items 的 exports,可以分别用下面两个命令看到。 DSPSRVPGM SRVPGM(SVRPGMNAME) DETAIL(*PROCEXP)DSPSRVPGM SRVPGM(SVRPGMNAME) DETAIL(*DTAEXP)如果会用 bind language 的话, 通过指定 exports 的名称或顺序的话, 在创建
22、service program时 export 选项用*srcfile, 可以避免因为 service program 里新增加 prodecure 而不得不重新编译所有使用到该 service program 的 programs 的问题。extproc 和 extpgm 的区别 =1.调用对象Extproc:main procedure 和 subprocedureExtpgm:OPM 和 ILE *pgm2.调用性质:Extproc:静态调用Extpgm:动态调用3.传参方式:Extproc:by reference、by read-only reference、by valueExtp
23、gm:by reference、by read-only reference4.示例:Extproc Demo:pass1.rpgleHNOMAIN /INCLUDE QCPYSRC,PASSBY P BYVALUE B EXPORT D BYVALUE PI 40A D PARM1 2P 0 D PARM2 20A VALUE C IF PARM1 = 1 C RETURN PARM2+LIKE RPG IIIC ELSE C EVAL PARM2 = LIKE RPG C RETURN PARM2+LIKE RPG IV C ENDIF P E =pass2.rpgleD AAA PR 4
24、0A EXTPROC(BYVALUE) D A1 2P 0 D A2 20A VALUE D NUM S 2P 0 D STRING S 20A INZ(KKK) D RESULT1 S 30A C EVAL NUM = 1 C EVAL RESULT1 = AAA(NUM:STRING)C RESULT1 DSPLY C EVAL *INLR = *ON C RETURN Extpgm Demo:DDS CODE:AT.Name+RLen+TDpBFunctions+A R ABCR A F1 10A =RPG CODE: FFilenameIPEAFLIDevice+KExit+EntrF
25、ABC O E DISK C *ENTRY PLIST C PARM MLNAME 10 C MOVELMLNAME F1 C WRITEABCR C SETON LR C RETRN =RPGLE CODE:DTESTADD PR EXTPGM(ADD) D 10A D PASSSTYLE PR D P2 10A D PASSSTYLE PI D P2 10A D P1 S 10A C C CALLP TESTADD(P2) C EVAL *INLR = *ON C RETURN 关于 UPDSRVPGM 后,新版本 module 立即生效的问题 问题: 做过 ILE 模式开发的同学都有过这
26、样的经历:已经执行过的 service program,重新编译了一个 module 后,并且更新了相应的 service program (UPDSRVPGM),然后再次调用这个 service program 下的这个 module,但是结果显示 module 并没有更新,举个例子:整个程序的组织结构关系,我描述的还清楚吧,现在执行:= =CALL PGM(PGMA),则会在屏幕上显示:AAA。下面把 MOD1 的功能改成:DSPLY BBB,即:然后重新编译 MOD1、更新 SRV0,重新执行 CALL PGM(PGMA)得出的结果仍然是:AAA 刚才明明重新创建了 MOD1,并且也进行
27、了 UPDSRVPGM,应该打印的是 BBB,为什么还是 AAA 呢?如何才能让程序调用最新的 Module 呢?解决办法:1 最简单粗暴直接的办法就是 sign off ,再重新登陆。2 用命令 RCLACTGRP ACTGRP(QILE)下面是我对此的理解(正确性有待验证):当更新一个 PGM(module)时,旧的程序版本会被系统移到 QRPLOBJ 这个 library 中。如果在更新PGM(module)时,PGM(module)恰好处于活动的状态,那么系统会自动让用户继续使用 QRPLOBJ 中的旧版本程序,而不会报错,当然用户也无法使用新版本的程序。当再次加载此程序的时候,才会使
28、用新版本的程序。 那什么时候会再次加载程序?对于 service program 而言:1 program 运行时系统会检查其 activation group 是否创建,若没有创建,则会创建 activation group。然后系统会在 activation group 中检查相关的 service program 是否已经被激活,如果service program 尚未被激活,则系统会激活 service program;如果 service program 已经被激活,就不会再次进行激活。简单的说 service program 只有在 PGM 首次运行的时候,才会被激活,并且此后这个
29、 service program 一直处于活动状态,直到 activation group 被删除。(RED BOOK)2 这段完全是我个人的猜想,没找到相关的理论做支撑。当更新 service program 时,因为service program 在第一次被调用的时候,已经被激活了,所以仅仅更新了 ASP(硬盘)中的 service program,而没有更新 activation group 中的 service program。这时候最直接的想法就是让activation group 重新加载 service program,这样就可以把 ASP 中新版本的 service progr
30、am 重新加载到 activation group 中了。但是系统并没有现成的命令,可以删除 activation group 中的service program 再进行重新加载,所以我们要用 SIGN OFF 和 RCLACTGRP 来先删除 activation group,这样 activation group 中的所有资源就都被释放掉了。然后 PGM A 再次运行的时候系统自动又会创建 activation group,然后会重新加载 service program,这样就会使用到新版本的service program 了。下图为我设想的简易流程图Data Area 和 Data Qu
31、eue 的区别分析(一) 网上流传一道题:data area 和 data queue 的区别。我个人觉得 data area 和 data queue 除了长的比较像之外,其可比性并不大。从大的方向说,data area 和 data queue 的共同点仅仅是二者都可以完成不同 job 间的通讯的功能(即数据交换),但是相同的功能还可以由 PF,user space 等完成,为什么没人把它们都拿来一起比较呢?因为长的不像?hoho下面分别介绍一下 Data Area 和 Data Queue,希望能给困惑者带来一点帮助,不对的地方欢迎指正。Data Area一 基本信息Data Area
32、在系统中用*DTAARA 来表示,实际上 OS400 把 Data Area 当作一个单独的字段。Data Area 中存储的数据类型有 3 种,分别是*CHAR、*DEC、*LGL。它们的最大的长度分别为:*CHAR2000 个字符*DEC 1 个数字(最长为 24 digits, 9 decimal positions。实际上长度不能超过 15)*LGL 1 个逻辑值(1或者0)由此可见,Data Area 所能保存的信息量十分有限,对于*DEC 和*LGL 来说,仅仅能保存一个数值;对与*CHAR 类型来说,能保存 2000 个字符的数据。所以,Data Area 不适用于大量数据的处理
33、。通常用 Data Area 来保存一些被多个 job 共用的控制信息。另外,Data Area 仅仅是一个单独的字段,不需要像 PF 中那样用游标来定位记录,所以打开一个 Data Area 的系统开销要比打开一个 PF 要小的多,效率高得多。这就是 Data Area 最大的优点。二 使用方法Data Area 的创建、删除、修改、显示可以用 CL 命令在命令行上操作,或者在 CLP 程序中操作,都比较简单,就不多说了。下面说一下在 RPGLE 中的使用方法。RPG 中关于 Data Area 的操作符有以下几个:IN、OUT、*LOCK、UNLOCK,IN:读取 Data AreaOUT
34、:输出更新 Data Area 并解锁*LOCK: 为 Data Area 加上锁UNLOCK:为 Data Area 解锁 下面举个例子来说明 Data Area 的基本操作:D Total DS DTAARA(TotDat) /定义 Data AreaD Amount 8 S 2OVERLAY(Total:1) C *LOCK IN Total /读取并且锁住 Data Area* / your codeC OUT Total /更新并且释放 Data Area 锁ORC *LOCK IN Total /读取并且锁住 Data Area* / your codeC UNLOCK Total
35、 /释放 Data Area 锁关于普通 Data Area 的锁,做个说明:1 Data Area 的锁是独占只读锁(exclusive, allow read),如果某 Data Area 已经被锁住了,那么其他的作业只能以只读的方式访问此 Data Area,不能用 OUT 操作。2 如果想在程序中更新 Data Area,则必须先锁住此 Data Area(跟 UPDATE PF 一个道理)。如果没有锁住 Data Area,就在那么在程序中用 OUT 的话,程序会报错,例如:C IN Total * / your code 没有锁住 data area 就输出,是不可以的。C OUT
36、 Total 3 Data Area 上锁可以通过1)使用*LOCK IN 方式 2)使用 Data Area Data Structure 方式(稍后介绍)4 Data Area 解锁可以通过1)OUT更新并解锁2)UNLOCK 解锁3)程序结束并且*INLR=*ON,则锁自然解除。三 Data Area Data Structure这种类型的 Data Area 有点特殊,使用起来会方便一些定义:D Total UDS DTAARA(TotDat) /定义 Data AreaD Amount 8 S 2OVERLAY(Total:1)Data Area Data Structure 与普通
37、的 Data Area 定义有一点差异,就是在 DS keyword 的前面加上一个 U,即:UDS使用方法:Data Area Data Structure 的特别之处就在于,它隐含了两行的代码,如下:C *LOCK IN Total /读取并且锁住 Data Area*C OUT Total /更新并且释放 Data Area 锁就是说如果定义成 Data Area Data Structure 的话,那么程序会自动读取并锁住此 Data Area,并且在程序结束的时候,会自动输出更新 Data Area 并释放锁。所以如果定义成 Data Area Data Structure 的话,就
38、不需要上面的两行代码了。四 *LDALocal Data Area*LDA 算是一种特殊的 Data Area,它是长度为 1024 个字节的字符串,并由系统自动为每个 job创建和删除。每个 job 只能操作自己的*LDA,不能操作、获取其他 job 的*LDA 的数据。就是说在同一个 job内,你愿意怎么折腾都行,但是你不能共享的 job 的*LDA,别的 JOB 也不能你的 JOB 的*LDA。不过有一种特例,即递交出去的作业是可以继承原来作业的*LDA 的内容。假如 JOB A 用 SBMJOB 递交出了一个 JOB B,那么系统会用 JOB A 的*LDA 的内容来初始化 JOB B
39、 的*LDA。因为*LDA 是私有的,不用担心其他 job 会锁住 LDA,所以 LDA 的读取和更新不用上锁,也不会报错。D Local DS DTAARA(*LDA) /定义*LDAD orderNum 5 Overlay(Local:5)*C IN LocalC EVAL orderNum=12345C OUT Local如果 Data Area 是*LDA 的话,不用锁住 Data Area,更新 Data Area 也不会报错,不是*LDA 的话,以上代码就会报错。数组与 DS 用法总结 ARRAY一 array 与 table 的区别Array:可以使用下标,进行顺序访问或随即访问
40、Table:不可以使用下标,只能顺序访问。只能以 TABxxx 开头。注:table 虽然和 array 的功能相似,但是 array 要比 table 灵活很多。Table 可以淘汰了,了解table 的原因是维护老的代码时可能会用到。以后就不说 table 了。二 array 的类型和 key word1 有三种类型的 array,分别是 compile time array、run time array、pre-run time array。2 与 array 相关的 key word:DIM(number) 定义数组中元素的数量ASCEND/DESCEND 元素在数组中升序/降序排序P
41、ERRCD(number) 对于 compile time array 每一行中元素的个数CTDATA 定义 compile time arrayFROMFILE/TOFILE pre-run time arrayEXTFMT 举例:/array1 定义 10 个元素,每个长度为 5D Array1 S 5 0 DIM(10)/array2 是 compile time array,共 6 行,每行 3 个元素,每个元素 10 个长度,升序排序D Array2 S 10 DIM(6) PERRCD(3) CTDATA ASCEND/compile time array 要在程序的结尾定义数据*
42、 CTDATA ARRAY2Element1 Element2 Element3Element4 Element5 Element6Element7 Element8 Element9Element10注:如果 compile time array 的数据发生变化,那么要更改程序源代码中的元素值,并要重新编译程序。在程序的末尾要有* CTDATA 。建议使用* CTDATA array_name 的方式,但是要注意array_name 要与 D-spec 定义的 array_name 要一致;仅用*CTDATA(不指定 array_name)时要注意* CTDATA 的数据按照 D-spec
43、定义的 array 的顺序进行匹配。并且两者不能混着用。3 Index:index 不能为 0 或负数,不能大于元素个数。三 array 相关的%BIF1 %Xfoot 数组求和Sum = %Xfoot(array_name)2 %LookUpxx 查找(%TLookUpxx for table)Index = %LookUpXX(argument:array:satrtindex)%LookUp 查找第一个=argument 元素的 index%LookUpLE 查找第一个的元素的 index%LookUpGE 查找第一个=argument 的元素的 index%LookUpGT 查找第一个
44、argument 的元素的 index注:除了%LookUp 外,使用其他的%LookUpxx 的数组必须是排过序的。3 %Size 返回变量或常量所占的字节数Size = %Size(name:*all)D SmallFiled S 5P 2D MyArray S 5 DIM(%Size(Smallfield)SizeSmall = %Size(SmallField) /SizeSmall = 3 ArraySize = %Size(MyArray) /ArraySize = 5 单个元素的 sizeArraysize = %Size(Myarray:*all) /Arraysize = 150 所有元素的 size4 %Elem 返回数组中元素的个数Number = %Elem(array_name)Data Structure一 DS 的 key wordOCCURS(number)