确定环境,z/OS UNIX 与 JCL 或 TSO
Identify environment, z/OS UNIX vs JCL or TSO
我可以从 C 程序内部调用什么函数,以发现程序 运行 在什么 z/OS 环境中,例如它是 z/OS UNIX(又名 USS)还是来自 TSO,比如通过 JCL?
有两种方法:CEE3INF和翻遍z/OS数据区。
CEE3INF 的优点是可以记录并可移植到任何 LE 环境,以及提供您在 z/OS 结构中不容易找到的有关 PIPI 的信息。
作为 CEE3INF 的替代方案,如果您只需要区分批处理、TSO、CICS 以及您是否被称为 USS 进程,系统数据区域中有大量信息。替代方法很简单,而且它在 LE 环境之外特别有用……尽管在 C 中甚至很容易,只需加载一些指针,您可以使用 XLC DSECT 到 C 结构转换实用程序获得这些指针。
TSO 地址 space 是 ASCBTSB 非零的地址 (PSAAOLD->ASCBTSB)。批处理作业是填写 ASCBJBNI (PSAAOLD->ASCBJBNI) 的作业。 CICS 地址 space 将 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 服务 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 上得到了相同的结果。
更新:"running in the z/OS UNIX Services environment" 是什么意思?
批处理作业、TSO 会话和启动任务很容易理解,但 "running in the z/OS UNIX Services environment" 是什么意思?在 CICS、IMS 或 WebSphere 等子系统中,"running under xxx" 很容易定义,因为事务 运行 包含在特殊类型的服务地址 space 中......但不幸的是,UNIX 服务不像那。
事实上,运行ning z/OS 上的几乎所有任务都可以使用 z/OS UNIX 服务,因此确实没有 "z/OS UNIX Services environment" 可以以传统方式定义。并行的是 VSAM...是一个打开 VSAM 文件的程序 "running in VSAM?"。我们可能会关心 运行ning IDCAMS 程序、打开 VSAM 文件的程序、使用 CICS/VSAM 的程序 - 但 "running in VSAM" 在没有进一步限定的情况下并不是特别有意义。此外,"running in VSAM" 并不排斥 运行ning 作为批处理、STC 或 TSO 用户 - 它与 z/OS UNIX 服务相同 - 你可以是批处理作业、启动任务或TSO 用户,并且您也可以是 "running in z/OS UNIX Services" 或不是。
这里是 "running in z/OS UNIX Services" 三个截然不同的定义:
- 工作单元是否已 "dubbed" 作为 UNIX 服务进程并因此准备好并能够请求 UNIX 服务内核功能。
- 工作单元是否在UNIX下运行ning shell,例如/bin/sh。
- LE 程序是否 运行 POSIX(ON) 运行时间选项。
为什么这很重要?好吧,某些软件——尤其是 运行time 库函数之类的东西被其他应用程序调用——根据调用者是否是 UNIX 进程而表现不同。
想象一下编写一个 "OPEN" 函数,该函数将文件名作为参数传递。如果您的调用方是 UNIX 进程,您可能会将文件名解释为实际文件名...OPEN(XYZ) 被解释为“检查当前工作目录中名为 'XYZ'[=81 的文件=]”。但是如果调用者没有被称为 UNIX 进程,那么 OPEN(XYZ) 可能意味着打开 'XYZ' DD 语句。您可以使用我上面概述的方法做出此决定,因为它告诉您您的任务实际上被称为 UNIX 进程。
好的,但这和上面的#2(运行ning 在 shell 下)有什么不同?
这是一个例子。假设您有一个可调用例程,它想要将消息写入输出文件。大多数非大型机 UNIX 应用程序将简单地写入 STDOUT 或 STDERR,但这并不总是适用于 z/OS,因为许多应用程序是 UNIX 进程,但它们不是 运行ning 在 [=133] =] - 如果没有 shell,STDOUT 和 STDERR 可能不存在。
场景如下...
您 运行 一个与 UNIX 服务无关的常规程序,但它做了一些事情使自己被称为 UNIX 进程。举个例子,也许有人将 "DD PATH=/some/unix/file" 放在一个古老的 COBOL 程序的 JCL 中......奇迹般地,当这个 COBOL 批处理作业 运行s 时,它是一个 UNIX 进程,因为它使用了UNIX 服务文件系统。
有很多东西可以让你的任务被称为 UNIX 进程......DD PATH 就是其中之一,但即使调用打开 TCP/IP 套接字或类似良性的东西的函数也可以做到这一点。也许您正在编写一个供应商产品,它只是一个批处理汇编程序,但它打开了一个 TCP/IP 套接字……这是 UNIX 进程的另一个常见示例 运行 没有 shell。
那么为什么这是个问题?好吧,想想如果可调用函数决定将它的消息写入 STDERR 会发生什么。也许它会测试它是否 运行ning 作为 UNIX 服务进程,如果是,它会写入 STDERR,否则它会动态分配并写入 SYSOUT 文件。听起来很简单,但它不适用于我的具有 DD PATH 的应用程序示例。
STDERR 从何而来?通常,UNIX shell 程序会设置它 - 当您 运行 shell 下的程序时,shell 通常会为您的程序传递三个预打开的 STDIN 文件句柄,标准输出和标准错误。由于在我的示例场景中没有 shell,这些文件句柄未传递给应用程序,因此写入 STDERR 将失败。事实上,除了STDIN/STDOUT/STDERR之外,shell传递给子进程的还有很多东西,比如环境变量、信号处理等等。 (当然,用户可以在他的 JCL 中手动分配 STDIN/STDOUT/STDERR...我在这里不谈这个)。
如果您希望软件能够处理 shell 下的 运行ning 而不是 shell 下的 运行ning,您还有更多工作要做而不仅仅是查看您的应用程序是否被称为 UNIX 进程:
- 检查您是否是 UNIX 进程...如果不是,则您不能 运行ning 在 shell.
下
- 检查您是否被 shell 启动。有多种方法可以做到这一点,但通常你会检查你的 "parent process" 或类似你传递的环境变量。这并不总是容易做到,因为 z/OS 上实际上有许多不同的 shell,所以您无法继续找到 "legitimate"。一种更可靠的方法是获取用户的登录名 shell 并进行检查。
- 作为检查父进程的替代方法,您可以直接检查所需的资源,例如在我的示例中针对 STDERR 文件句柄调用 ioctl()。当然,这可能是危险的......想象一下应用程序打开几个套接字并调用你的函数的情况......你认为真正的 STDIN/STDOUT/STDERR 实际上可能是你的调用者设置的打开文件句柄,你写的东西很容易破坏他的数据。
至于我的第三个例子 - LE 程序 运行 POSIX(ON) - 对于使用基于 LE 运行time 的高级语言编写的开发人员来说,这主要是一个问题, 因为某些 运行 时间函数的行为与 POSIX(ON) 或 POSIX(OFF) 不同。
一个例子是 C 程序员编写了一个可以被 POSIX(ON) 和 POSIX(OFF) 调用者调用的函数。假设函数想要在单独的线程下进行一些后台处理...在 POSIX(ON) 应用程序中,开发人员可能会使用 pthread_create(),但这在 [=123] 中不起作用=](关闭)。实际上在 IBM 的 LE 运行time 中有很多东西根据 POSIX 设置表现不同:线程、信号处理等等等等。如果你希望编写 "universal" 代码并且你需要这些功能,您肯定需要在执行时查询 POSIX 设置,并根据其设置方式采取不同的路径。
希望这能揭示隐藏在这个问题背后的复杂性……"running in z/OS UNIX environment" 的三个不同定义,以及说明每个用例为何重要的三个不同用例。
我可以从 C 程序内部调用什么函数,以发现程序 运行 在什么 z/OS 环境中,例如它是 z/OS UNIX(又名 USS)还是来自 TSO,比如通过 JCL?
有两种方法:CEE3INF和翻遍z/OS数据区。
CEE3INF 的优点是可以记录并可移植到任何 LE 环境,以及提供您在 z/OS 结构中不容易找到的有关 PIPI 的信息。
作为 CEE3INF 的替代方案,如果您只需要区分批处理、TSO、CICS 以及您是否被称为 USS 进程,系统数据区域中有大量信息。替代方法很简单,而且它在 LE 环境之外特别有用……尽管在 C 中甚至很容易,只需加载一些指针,您可以使用 XLC DSECT 到 C 结构转换实用程序获得这些指针。
TSO 地址 space 是 ASCBTSB 非零的地址 (PSAAOLD->ASCBTSB)。批处理作业是填写 ASCBJBNI (PSAAOLD->ASCBJBNI) 的作业。 CICS 地址 space 将 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 服务 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 上得到了相同的结果。
更新:"running in the z/OS UNIX Services environment" 是什么意思?
批处理作业、TSO 会话和启动任务很容易理解,但 "running in the z/OS UNIX Services environment" 是什么意思?在 CICS、IMS 或 WebSphere 等子系统中,"running under xxx" 很容易定义,因为事务 运行 包含在特殊类型的服务地址 space 中......但不幸的是,UNIX 服务不像那。
事实上,运行ning z/OS 上的几乎所有任务都可以使用 z/OS UNIX 服务,因此确实没有 "z/OS UNIX Services environment" 可以以传统方式定义。并行的是 VSAM...是一个打开 VSAM 文件的程序 "running in VSAM?"。我们可能会关心 运行ning IDCAMS 程序、打开 VSAM 文件的程序、使用 CICS/VSAM 的程序 - 但 "running in VSAM" 在没有进一步限定的情况下并不是特别有意义。此外,"running in VSAM" 并不排斥 运行ning 作为批处理、STC 或 TSO 用户 - 它与 z/OS UNIX 服务相同 - 你可以是批处理作业、启动任务或TSO 用户,并且您也可以是 "running in z/OS UNIX Services" 或不是。
这里是 "running in z/OS UNIX Services" 三个截然不同的定义:
- 工作单元是否已 "dubbed" 作为 UNIX 服务进程并因此准备好并能够请求 UNIX 服务内核功能。
- 工作单元是否在UNIX下运行ning shell,例如/bin/sh。
- LE 程序是否 运行 POSIX(ON) 运行时间选项。
为什么这很重要?好吧,某些软件——尤其是 运行time 库函数之类的东西被其他应用程序调用——根据调用者是否是 UNIX 进程而表现不同。
想象一下编写一个 "OPEN" 函数,该函数将文件名作为参数传递。如果您的调用方是 UNIX 进程,您可能会将文件名解释为实际文件名...OPEN(XYZ) 被解释为“检查当前工作目录中名为 'XYZ'[=81 的文件=]”。但是如果调用者没有被称为 UNIX 进程,那么 OPEN(XYZ) 可能意味着打开 'XYZ' DD 语句。您可以使用我上面概述的方法做出此决定,因为它告诉您您的任务实际上被称为 UNIX 进程。
好的,但这和上面的#2(运行ning 在 shell 下)有什么不同?
这是一个例子。假设您有一个可调用例程,它想要将消息写入输出文件。大多数非大型机 UNIX 应用程序将简单地写入 STDOUT 或 STDERR,但这并不总是适用于 z/OS,因为许多应用程序是 UNIX 进程,但它们不是 运行ning 在 [=133] =] - 如果没有 shell,STDOUT 和 STDERR 可能不存在。
场景如下...
您 运行 一个与 UNIX 服务无关的常规程序,但它做了一些事情使自己被称为 UNIX 进程。举个例子,也许有人将 "DD PATH=/some/unix/file" 放在一个古老的 COBOL 程序的 JCL 中......奇迹般地,当这个 COBOL 批处理作业 运行s 时,它是一个 UNIX 进程,因为它使用了UNIX 服务文件系统。 有很多东西可以让你的任务被称为 UNIX 进程......DD PATH 就是其中之一,但即使调用打开 TCP/IP 套接字或类似良性的东西的函数也可以做到这一点。也许您正在编写一个供应商产品,它只是一个批处理汇编程序,但它打开了一个 TCP/IP 套接字……这是 UNIX 进程的另一个常见示例 运行 没有 shell。
那么为什么这是个问题?好吧,想想如果可调用函数决定将它的消息写入 STDERR 会发生什么。也许它会测试它是否 运行ning 作为 UNIX 服务进程,如果是,它会写入 STDERR,否则它会动态分配并写入 SYSOUT 文件。听起来很简单,但它不适用于我的具有 DD PATH 的应用程序示例。
STDERR 从何而来?通常,UNIX shell 程序会设置它 - 当您 运行 shell 下的程序时,shell 通常会为您的程序传递三个预打开的 STDIN 文件句柄,标准输出和标准错误。由于在我的示例场景中没有 shell,这些文件句柄未传递给应用程序,因此写入 STDERR 将失败。事实上,除了STDIN/STDOUT/STDERR之外,shell传递给子进程的还有很多东西,比如环境变量、信号处理等等。 (当然,用户可以在他的 JCL 中手动分配 STDIN/STDOUT/STDERR...我在这里不谈这个)。
如果您希望软件能够处理 shell 下的 运行ning 而不是 shell 下的 运行ning,您还有更多工作要做而不仅仅是查看您的应用程序是否被称为 UNIX 进程:
- 检查您是否是 UNIX 进程...如果不是,则您不能 运行ning 在 shell. 下
- 检查您是否被 shell 启动。有多种方法可以做到这一点,但通常你会检查你的 "parent process" 或类似你传递的环境变量。这并不总是容易做到,因为 z/OS 上实际上有许多不同的 shell,所以您无法继续找到 "legitimate"。一种更可靠的方法是获取用户的登录名 shell 并进行检查。
- 作为检查父进程的替代方法,您可以直接检查所需的资源,例如在我的示例中针对 STDERR 文件句柄调用 ioctl()。当然,这可能是危险的......想象一下应用程序打开几个套接字并调用你的函数的情况......你认为真正的 STDIN/STDOUT/STDERR 实际上可能是你的调用者设置的打开文件句柄,你写的东西很容易破坏他的数据。
至于我的第三个例子 - LE 程序 运行 POSIX(ON) - 对于使用基于 LE 运行time 的高级语言编写的开发人员来说,这主要是一个问题, 因为某些 运行 时间函数的行为与 POSIX(ON) 或 POSIX(OFF) 不同。
一个例子是 C 程序员编写了一个可以被 POSIX(ON) 和 POSIX(OFF) 调用者调用的函数。假设函数想要在单独的线程下进行一些后台处理...在 POSIX(ON) 应用程序中,开发人员可能会使用 pthread_create(),但这在 [=123] 中不起作用=](关闭)。实际上在 IBM 的 LE 运行time 中有很多东西根据 POSIX 设置表现不同:线程、信号处理等等等等。如果你希望编写 "universal" 代码并且你需要这些功能,您肯定需要在执行时查询 POSIX 设置,并根据其设置方式采取不同的路径。
希望这能揭示隐藏在这个问题背后的复杂性……"running in z/OS UNIX environment" 的三个不同定义,以及说明每个用例为何重要的三个不同用例。