在软件没有将所有可能的情况都考虑在内时,在执行程序期间就可能发生异常。这些异常和中断导致系统进入了一种需要软件和硬件协同工作来做出决策的状态。对于一个超级标量管道化的Z 处理器,系统通常执行以下基本步骤来查找异常的解决方案:
old_PSW + instruction_length
重新启动该程序。 尽管是硬件在控制异常处理的大部分过程,但开发人员可控制异常中断或异常出口的结果。
一些语言提供了异常处理功能。C++ 提供了抛接机制,而 C 通过 signal.h 中定义的例程来帮助开发人员。Metal C 用户拥有使用 zOS 所提供的丰富的汇编器服务宏来自定义异常的控制和结果的优势。
服务宏类似于其他 HLASM 指令。它们可在编译程序后使用 –qMETAL
和 –S
选项插入到生成的汇编 (.s) 文件中。有两个系统宏可帮助您处理两个级别的异常:
可使用 ESPIE 处理的基本程序中断包括以下 15 种中断代码,从 01 到 0F:
表 1. 基本中断
标识 | 中断代码 | |
---|---|---|
二进制值 | 十六进制值 | |
Operation | P0000001 | 0001 |
Privileged operation | P0000010 | 0002 |
Execute | P0000011 | 0003 |
Protection | P0000100 | 0004 |
Addressing | P0000101 | 0005 |
Specification | P0000110 | 0006 |
Data | P0000111 | 0007 |
Fixed-pt overflow | P0001000 | 0008 |
Fixed-pt divide | P0001001 | 0009 |
Decimal overflow | P0001010 | 000a |
Decimal divide | P0001011 | 000b |
HFP exp. overflow | P0001100 | 000c |
HFP exp. underflow | P0001101 | 000d |
HFP significance | P0001110 | 000e |
HFP divide | P0001111 | 000f |
发生异常时使用 ESPIE SET 宏将控制权提供给一个出口例程并建立 Program Interruption Exit。例如,要将所有 15 个中断的控制权提供给一个名为 EXIT 的出口例程:
ESPIE SET,EXIT,((1,15))
第一个操作数 SET
建立出口。第二个操作数是出口例程的名称。第三也是最后一个操作数指定中断处理类型。格式 ((1,15))
表示从 1 到 15 的所有类型。如果想要处理特定的类型,可使用 (1,15)
仅处理类型 1 和 15。
这个宏可扩展为以下指令:
清单 1. ESPIE SET 宏
11 ESPIE SET,EXIT,((1,15)) 12+* MACDATE = xx/xx/xx 13+ CNOP 0,4 14+ BAS 1,*+20 15+IHB00002 EQU * 16+ DC A(EXIT) EXIT ROUTINE ADDRESS 17+ DC A(0) PARAMETER LIST ADDRESS 18+ DC B?0111111111111111? INTERRUPTION MASK 19+ DC B?0000000000000000? 20+ DC A(0) PLACE HOLDER 21+ LA 0,4 FUNCTION CODE 22+ LA 15,28 23+ SVC 109 24 ST 1,token Save EPIE
您想要涵盖的 15 种中断中每种类型对应于语句 19 中的 1 位。对于本例,我想涵盖所有 15 种类型。在调用 ESPIE 宏时创建了一个扩展的程序中断元素。它包含有关中断的数据。该数据保存在一个名为程序中断控制区 (PICA) 的区域中。该区域的地址返回到寄存器 1 中。在上面的例子中,寄存器 1 保存在 token
中。控制权传递到出口例程时,系统将表 2 中所示的信息放在 EPIE 的 PICA 中。
注意:我们仅给出了有用的信息,完整的 EPIE 可在 MVS 数据区域第 2 卷中找到,参见参考资料获取链接。
表 2. 有用的 PICA 信息
偏移 | 类型 | 长度 | 名称 | 描述 | |
---|---|---|---|---|---|
十进制 | 十六进制 | ||||
0 | 0 | 字符串 | 4 | EPIEEPIE | EPIE 块标识符 C'EPIE' |
4 | 4 | 地址 | 4 | EPIEPARM | 参数列表地址 |
8 | 8 | 字符串 | 64 | EPIEGPR | 中断时为 32 位 GPR |
72 | 48 | 字符串 | 8 | EPIEPSW | 中断时为 EC 模式 8 字节旧 PSW |
74 | 4A | 位串 (Bitstring) | 1 | EPIECCPM | 条件代码和程序掩码 |
76 | 4c | 地址 | 4 | EPIENXT1 | 下一个指令的地址 |
81 | 51 | 位串 | 1 | EPIEILC1 | 指令长度代码 |
83 | 53 | 位串 | 1 | EPIEICD1 | 程序中断代码 |
160 | A0 | 字符串 | 128 | EPIEG64 | 中断时为 64 位 GPR |
296 | 128 | 字符串 | 16 | EPIEPS16 | 中断之前的 16 字节 PSW |
您可通过提供该地址在寄存器 1 中的编译来访问表 2 中的信息。指令长度代码和旧 PSW 在编写出口例程时很有用。该用法已在更高级的出口中讨论。
回页首
控制权传递到出口例程时,系统会分配寄存器。如果出口例程包含 I/O 操作,系统会保留以下寄存器:
清单 2 中使用了一个简单的例子来演示如何使用 ESPIE SET 优雅地退出一个发生除以 0 的异常的程序。
清单 2. 主要程序
int main(){ int divident=10; int divisor=0; int quotient=divident/divisor; return 0; }
使用 MetalC 选项编译而不使用优化级别,会得到汇编代码 fun.s(参见下载获取 fun.s 文件)。
注意:未包含函数属性和 post-fix 代码块,因为它们不相关。如果汇编和运行此程序,系统会由于定点除法异常而终止该程序。
您首先希望此程序优雅地退出并返回值 0,无论发生何种异常。为了实现此目标,在进入 MAIN 例程之前在 fun.s 开头插入 ESPIE SET 宏:
清单 3. ESPIE 宏初始化
USING *,15 ESPIE SET,EXIT,((1,15)) DROP 15 J MAIN
将例程 EXIT 添加到主要例程(在 fun.s 中标为 @@LAB@2)之后:
清单 4. 返回 0 的出口例程
EXIT DS 0H BR 14
您只需分支到 GR14,因为 GR14 包含返回地址,而且您只希望返回该地址而不更改返回代码。汇编并运行该程序会获得返回代码 0 和一个正常的出口。也可向 EXIT 添加额外的代码来让该程序返回一个不同的代码(例如 55),并释放以前分配的任何存储,比如清单 5。
清单 5. 返回 55 的出口例程
EXIT DS 0F DROP LR 3,13 L 13,4(,13) ST 15,16(,13) RETURN LARL 15,RETURN USING RETURN,15 STORAGE RELEASE,LENGTH=160,ADDR=(3) DROP 15 L 15,16(,13) LA 15,55 return 55 when interruption happens L 14,12(,13) BR 14 DS 0F
回页首
除了清单 5 中所示的简单出口,也可为出口使用更复杂的例程,包括:
回页首
在本文中,您了解了处理基本系统中断和异常的技术。在使用 Metal C 编程期间使用 zOS 汇编器服务,而且没有特定于语言的库来提供帮助时,可能在 zOS 上会发生这种情况。您了解了如何利用 zOS 所提供的汇编器服务宏,创建低级异常处理例程来更改程序对异常和中断的反应。
回页首
感谢 Visda Vokhshoori,他的技术建议对本文的构想很有帮助。
回页首
描述 | 名字 | 大小 |
---|---|---|
汇编代码 | fun.zip | 1KB |