c-确定环境,z/OS UNIX与JCL或TSO



我可以从C程序内部调用什么函数来发现程序运行在什么z/OS环境中,例如,它是z/OS UNIX(又名USS)还是来自TSO,例如通过JCL?

有两种方法: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 %dn", 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,则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上得到了相同的结果。

更新:"在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服务中运行"的三个截然不同的定义:

  1. 工作单元是否已被"命名"为UNIX服务进程,因此是否准备好并能够请求UNIX服务内核功能
  2. 工作单元是否在UNIX shell下运行,例如/bin/sh
  3. LE程序是否使用POSIX(ON)运行时选项运行

为什么这些都很重要?好吧,有些软件——尤其是其他应用程序调用的运行库函数——的行为会有所不同,这取决于调用方是否是UNIX进程。

想象一下,编写一个"OPEN"函数,将文件名作为参数传递。如果调用方是UNIX进程,则可以将文件名解释为实际的文件名。。。OPEN(XYZ)被解释为"检查当前工作目录中名为"XYZ"的文件"。但是,如果调用方没有被称为UNIX进程,那么OPEN(XYZ)可能意味着打开"XYZ"DD语句。您可以使用上面概述的方法来做出这个决定,因为它告诉您的任务实际上被称为UNIX进程。

好吧,但这和上面的#2(在外壳下运行)有什么不同?

这里有一个例子。假设您有一个可调用例程,它想要将消息写入输出文件。大多数非大型机UNIX应用程序只需写入STDOUT或STDERR,但这在z/OS上并不总是有效的,因为许多应用程序都是UNIX进程,但它们不是在shell下运行的——如果没有shell,STDOUT和STDERR可能就不存在。

以下是场景。。。

您运行一个与UNIX服务无关的传统程序,但它做了一些事情,将自己称为UNIX进程。举个例子,也许有人把"DDPATH=/some/unix/file"放在一个古老的COBOL程序的JCL中。。。奇迹般的是,当这个COBOL批处理作业运行时,它是一个UNIX进程,因为它使用了UNIX服务文件系统。有很多事情可以将您的任务称为UNIX进程。。。DDPATH就是其中之一,但即使调用一个打开TCP/IP套接字的函数或类似的良性函数也可以做到这一点。也许你正在编写一个供应商产品,它只是一个批处理汇编程序,但它打开了一个TCP/IP套接字。。。这是在没有shell的情况下运行的UNIX进程的另一个常见示例。

那么为什么这是个问题呢?好吧,想想如果那个可调用函数决定将它的消息写入STDERR会发生什么。也许它会测试它是否作为UNIX服务进程运行,如果是,它会写入STDERR,否则它会动态分配并写入SYSOUT文件。听起来很简单,但它不适用于我的应用程序具有DD PATH的例子。

STDERR来自哪里?通常情况下,UNIXshell程序会设置它——当您在shell下运行程序时,shell通常会向您的程序传递三个预打开的文件句柄STDIN、STDOUT和STDERR。由于在我的示例场景中没有shell,所以这些文件句柄没有传递给应用程序,所以对STDERR的写入将失败。事实上,除了STDIN/STDOUT/STDERR之外,shell还传递给子进程很多东西,比如环境变量、信号处理等等。(当然,用户可以在他的JCL中手动分配STDIN/STDOUT/STDERR……我在这里不讨论这个)。

如果您想要一个既能处理在shell下运行又能处理不在shell上运行的软件,那么您需要做的工作就不仅仅是查看您的应用程序是否被称为UNIX进程:

  • 检查您是否是UNIX进程。。。如果没有,你就不可能在壳下运行
  • 检查一下你是否被炮弹击中。有多种方法可以做到这一点,但通常情况下,您要检查您的"父进程"或类似于传递给您的环境变量的内容。这并不总是容易做到的,因为z/OS上实际上有很多不同的shell,所以没有太多可以找到"合法"的shell。其中一种更具防弹性的方法是为用户获取登录外壳并进行检查
  • 作为检查父进程的另一种选择,您可以直接检查所需的资源,例如通过对STDERR文件句柄调用ioctl(),如我的示例所示。当然,这可能很危险。。。想象一下应用程序打开几个套接字并调用函数的情况。。。您认为真正的STDIN/STDOUT/STDERR实际上可能是由调用者设置的打开文件句柄,而您所写的内容可能很容易破坏他的数据

至于我的第三个例子——用POSIX(ON)运行的LE程序——这在很大程度上是开发人员在基于LE运行时的高级语言中编写的一个问题,因为某些运行时函数的行为与POSIX(ON)或POSIX(OFF)不同。

一个例子是C程序员编写一个函数,该函数可以由POSIX(ON)和POSIX(OFF)调用方调用。假设函数想在一个单独的线程下进行一些后台处理。。。在POSIX(ON)应用程序中,开发人员可能会使用pthread_create(),但这在POSIX中不起作用。事实上,在IBM的LE运行时中,有很多东西的行为因POSIX设置的不同而不同:线程、信号处理等。如果你希望编写"通用"代码,并且你需要这些函数,那么你肯定需要在执行时查询POSIX设置,并根据其设置方式采取不同的路径。

因此,希望这能揭示这个问题背后的复杂性。。。"在z/OS UNIX环境中运行"的三种不同定义,以及说明为什么每种定义都很重要的三个不同用例。

最新更新