我可以从 C 程序内部调用什么函数,以发现程序在哪个 z/OS 环境中运行,例如它是 z/OS UNIX(又名 USS)还是来自 TSO,比如通过 JCL?
1 回答
有两种方法:CEE3INF和翻阅 z/OS 数据区。
CEE3INF 具有文档化和可移植到任何 LE 环境的优势,以及提供在 z/OS 结构中不容易找到的有关 PIPI 的信息。
作为 CEE3INF 的替代方案,如果您只需要区分 Batch、TSO、CICS 以及您是否被称为 USS 流程,系统数据区域中有大量信息。替代方法很简单,并且在 LE 环境之外特别有用……尽管在 C 中甚至可以很容易地通过加载一些指针来实现,这些指针可以通过使用 XLC DSECT 到 C 结构转换实用程序获得。
TSO 地址空间是 ASCBTSB 非零 (PSAAOLD->ASCBTSB) 的地址空间。批处理作业是填写 ASCBJBNI (PSAAOLD->ASCBJBNI) 的作业。CICS 地址空间的 TCBCAUF 设置为非零 (PSATOLD->TCBCAUF)。
在上述任何情况下,您还可以通过检查 TCB->STCB->STCBOTCB 来检查您的任务是否被称为 UNIX 进程。如果非零,则您已被配音并且可以使用 UNIX 服务。OTCBPRLI 字段有 PID 等进程信息,THLI 有线程级信息。
请注意,给定任务可能有资格使用 USS 函数,但目前还没有。“querydub()”函数可以帮助您区分已经配音的任务与可以配音但尚未配音的任务。
如果您使用 CEE3INF,有一些评论说它在 main() 函数之外无法正常工作,但我认为问题是 IBM 在其文档中提供的示例中的一个小错误。此示例在我的 z/OS 2.3 和 2.4 系统上运行良好:
#include <leawi.h>
#include <string.h>
#include <ceeedcct.h>
int do_call(void)
{
_INT4 sys_subsys,env_info,member_id,gpid;
_FEEDBACK fc;
CEE3INF(&sys_subsys,&env_info,&member_id,&gpid,&fc);
if ( _FBCHECK(fc,CEE000) != 0 )
{
printf("CEE3INF failed with message number %d\n", fc.tok_msgno);
}
printf("System/Subsystem in hex %08x \n",sys_subsys);
printf("Enviornment info in hex %08x \n",env_info);
printf("Member languages in hex %08x \n",member_id);
printf("GPID information in hex %08x \n",gpid);
printf("\n");
}
int main(void)
{
do_call();
}
这是 IBM 手册中的示例代码,除了调用 CEE3INF 中的注意事项外,IBM 文档有一个错误(“...fc”而不是“...&fc”)。如果在 main() 之外调用,有关于 CEE3INF 不起作用的评论,但我认为问题只是上面示例中的错误。
为了测试,我使用以下命令在 UNIX Services shell 下编译上面的代码:
xlc -o testinf testinf.c
然后我从 z/OS shell 会话运行可执行文件:
> ./testinf
System/Subsystem in hex 02000002
Enviornment info in hex 00540000
Member languages in hex 10000000
GPID information in hex 04020300
这是 z/OS 2.3 系统 - 我在 2.4 上得到相同的结果。
更新:“在 z/OS UNIX 服务环境中运行”是什么意思?
批处理作业与 TSO 会话与已启动的任务很容易理解,但“在 z/OS UNIX 服务环境中运行”是什么意思?在 CICS、IMS 或 WebSphere 等子系统中,“在 xxx 下运行”很容易定义,因为事务在特殊类型的服务地址空间内运行……但不幸的是,UNIX 服务不是这样的。
实际上,几乎任何在 z/OS 上运行的任务都可以使用 z/OS UNIX 服务,因此实际上没有可以以传统方式定义的“z/OS UNIX 服务环境”。并行将是 VSAM ...是一个打开 VSAM 文件的程序“在 VSAM 中运行?”。我们可能关心运行 IDCAMS 的程序、打开 VSAM 文件的程序、使用 CICS/VSAM 的程序——但如果没有进一步的限定,“在 VSAM 中运行”并不是特别有意义。此外,“在 VSAM 中运行”并非以批处理、STC 或 TSO 用户身份运行 - 它与 z/OS UNIX 服务相同 - 您可以是批处理作业、已启动任务或 TSO 用户,您还可以是否“在 z/OS UNIX 服务中运行”。
以下是“在 z/OS UNIX 服务中运行”的三个非常不同的定义:
- 工作单元是否已被“配音”为 UNIX 服务进程,因此是否已准备好并能够请求 UNIX 服务内核功能。
- 工作单元是否在 UNIX shell 下运行,例如 /bin/sh。
- LE 程序是否使用 POSIX(ON) 运行时选项运行。
为什么这有什么关系?嗯,一些软件——尤其是像其他应用程序调用的运行时库函数——根据调用者是否是 UNIX 进程而表现不同。
想象一下编写一个“OPEN”函数,该函数将文件名作为参数传递。如果您的调用者是 UNIX 进程,您可能会将文件名解释为实际文件名...OPEN(XYZ) 被解释为“检查当前工作目录中是否存在名为 'XYZ' 的文件”。但是如果调用者不被称为 UNIX 进程,那么 OPEN(XYZ) 可能意味着打开 'XYZ' DD 语句。您可以使用我上面概述的方法来确定,因为它告诉您您的任务实际上被称为 UNIX 进程。
好的,但是这和上面的#2 有什么不同(在 shell 下运行)?
这是一个例子。假设您有一个可调用例程,想要将消息写入输出文件。大多数非大型机 UNIX 应用程序会简单地写入 STDOUT 或 STDERR,但这并不总是适用于 z/OS,因为许多应用程序是 UNIX 进程,但它们不在 shell 下运行 - 并且没有 shell、STDOUT 和 STDERR可能不存在。
这是场景...
您运行一个与 UNIX 服务无关的常规程序,但它会做一些事情以使自己被称为 UNIX 进程。举个例子,也许有人将“DD PATH=/some/unix/file”放在一个古老的 COBOL 程序的 JCL 中......奇迹般地,当这个 COBOL 批处理作业运行时,它是一个 UNIX 进程,因为它利用UNIX 服务文件系统。有很多事情可以让您的任务被称为 UNIX 进程...DD PATH 就是其中之一,但即使调用打开 TCP/IP 套接字或类似良性的函数也可以解决问题。也许您正在编写一个供应商产品,它只是一个批处理汇编程序,但它打开了一个 TCP/IP 套接字……这是另一个在没有外壳的情况下运行的 UNIX 进程的常见示例。
那么为什么这是一个问题呢?好吧,想想如果该可调用函数决定将它的消息写入 STDERR 会发生什么。也许它会测试它是否作为 UNIX 服务进程运行,如果是,它会写入 STDERR,否则它会动态分配并写入 SYSOUT 文件。听起来很简单,但它不适用于我的具有 DD PATH 的应用程序示例。
STDERR 来自哪里?通常,UNIX shell 程序会设置它——当您在 shell 下运行程序时,shell 通常会向您的程序传递三个预先打开的文件句柄,即 STDIN、STDOUT 和 STDERR。由于在我的示例场景中没有 shell,这些文件句柄没有传递给应用程序,因此对 STDERR 的写入将失败。实际上,除了STDIN/STDOUT/STDERR,shell 传递给子进程的东西还有很多,比如环境变量、信号处理等等。(当然,用户可以在他的 JCL 中手动分配 STDIN/STDOUT/STDERR ......我不是在这里谈论)。
如果您想拥有既可以在 shell 下运行又可以在 shell 下不运行的软件,您需要做的不仅仅是查看您的应用程序是否被称为 UNIX 进程:
- 检查你是否是一个 UNIX 进程……如果不是,你不能在 shell 下运行。
- 检查您是否是由 shell 启动的。有多种方法可以做到这一点,但通常您正在检查您的“父进程”或您传递的环境变量之类的东西。这并不总是容易做到的,因为在 z/OS 上实际上有许多不同的 shell,所以你没有太多可以继续发现“合法”的 shell。更防弹的方法之一是为用户获取登录 shell 并检查它。
- 作为检查父进程的替代方法,您可以直接检查您需要的资源,例如在我的示例中针对 STDERR 文件句柄调用 ioctl()。当然,这可能很危险...想象一下应用程序打开几个套接字并调用您的函数的情况...您认为真正的 STDIN/STDOUT/STDERR 实际上可能是您的调用者设置的打开文件句柄,你写的东西很容易破坏他的数据。
至于我的第三个示例 - 使用 POSIX(ON) 运行的 LE 程序 - 对于使用基于 LE 运行时的高级语言编写的开发人员来说,这在很大程度上是一个问题,因为某些运行时函数的行为与 POSIX(ON) 或 POSIX 不同(离开)。
一个例子是 C 程序员编写了一个可以被 POSIX(ON) 和 POSIX(OFF) 调用者调用的函数。假设该函数想要在单独的线程下进行一些后台处理……在 POSIX(ON) 应用程序中,开发人员可能会使用 pthread_create(),但这在 POSIX(OFF) 中不起作用。实际上,IBM 的 LE 运行时中有很多东西会根据 POSIX 设置表现不同:线程、信号处理等。如果您希望编写“通用”代码并且需要这些功能,那么您肯定需要查询执行时的 POSIX 设置,并根据其设置方式采用不同的路径。
因此,希望这能阐明隐藏在这个问题背后的复杂性……“在 z/OS UNIX 环境中运行”的三个不同定义,以及三个不同的用例说明了为什么每个用例都很重要。