转载

处理在 zOS 上使用 Metal C 编程时的基本异常

在软件没有将所有可能的情况都考虑在内时,在执行程序期间就可能发生异常。这些异常和中断导致系统进入了一种需要软件和硬件协同工作来做出决策的状态。对于一个超级标量管道化的Z 处理器,系统通常执行以下基本步骤来查找异常的解决方案:

  1. 在硬件中检测到异常。
  2. 处理器控件停止在执行管道阶段导致异常的指令。
  3. 在异常指令正常完成之前,抓取并解码指令。
  4. 管道控件(调度程序)擦除目前管道中在异常指令之后的指令。
  5. 处理器获取当前管道状态的 “快照”。
  6. 旧程序状态字 (PSW) 所指向的异常指令的地址存储在存储的前 8K 中。
  7. 硬件将控制权传递给 OS 的异常处理程序。
  8. 异常处理程序检查异常并执行各种操作,比如:
    • 如果异常无法通过默认方法处理,则创建异常中止和核心转储事件。
    • 如果异常是 I/O 中断或有效的页错误,则首先处理中断。然后,还原处理器状态来从 old_PSW + instruction_length 重新启动该程序。

尽管是硬件在控制异常处理的大部分过程,但开发人员可控制异常中断或异常出口的结果。

zOS 提供的功能

一些语言提供了异常处理功能。C++ 提供了抛接机制,而 C 通过 signal.h 中定义的例程来帮助开发人员。Metal C 用户拥有使用 zOS 所提供的丰富的汇编器服务宏来自定义异常的控制和结果的优势。

程序中断

服务宏类似于其他 HLASM 指令。它们可在编译程序后使用 –qMETAL–S 选项插入到生成的汇编 (.s) 文件中。有两个系统宏可帮助您处理两个级别的异常:

  • 使用一个 Program Interruption Exit(用于 31 位的 ESPIE FOR 或用于 24 位的 SPIE)宏将执行重定向到一个用户出口,以处理基本程序中断。本文解释了此过程。
  • 使用一个 Task Abnormal Termination Exit (ESTEAX) 调查任何异常中止。ESTEAX 在发生异常中止时接收控制权并重定向到一个用户例程来采取正确的操作。因为该接口比 ESPIE 更复杂,所以它将是另一篇文章的主题。

可使用 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 操作,系统会保留以下寄存器:

  • GPR 0:由系统用作工作寄存器。
  • GPR 1:导致中断的任务的 PIE 或 EPIE 的地址。
  • GPR 14:返回地址
  • GPR 15:出口例程的地址

清单 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 中所示的简单出口,也可为出口使用更复杂的例程,包括:

  • 打印一条错误消息并在下一个指令的地址处恢复执行。
  • 如果中断是由无效数据导致的,您可在通过从旧 PSW 中减去表 2 中提供的指令长度代码 (ILC) 来更正数据后发出重试命令。
    注意 :使用重试命令时请小心,不要使程序进入中断循环。
  • 修改旧 PSW 或仅执行简单的 JUMP 操作跳到另一个标签。这会在程序的一个不同位置继续控制,进而跳过错误指令。

回页首

结束语

在本文中,您了解了处理基本系统中断和异常的技术。在使用 Metal C 编程期间使用 zOS 汇编器服务,而且没有特定于语言的库来提供帮助时,可能在 zOS 上会发生这种情况。您了解了如何利用 zOS 所提供的汇编器服务宏,创建低级异常处理例程来更改程序对异常和中断的反应。

回页首

致谢

感谢 Visda Vokhshoori,他的技术建议对本文的构想很有帮助。

回页首

下载

描述 名字 大小
汇编代码 fun.zip 1KB
正文到此结束
Loading...