有两种可能……
对于不熟悉分区数据集(即 PDS 或 PDS/E)内部结构的任何人,这些数据集在逻辑上分为两部分:一个“目录”具有指向所有单个成员的指针,以及一个“数据" 包含各个成员的实际记录的区域:
PDS: <DIRECTORY BLOCKS>
<MEMBER1>: ADDRESS OF DATA FOR MEMBER1 (xxx)
<MEMBER2>: ADDRESS OF DATA FOR MEMBER2 (yyy)
...
<DIRECTORY FREESPACE)
...
<EOF - END OF THE PDS DIRECTORY>
<DATA PORTION>
+xxx = DATA FOR MEMBER1
...
<EOF - END OF MEMBER1>
+yyy = DATA FOR MEMBER2
...
<EOF - END OF MEMBER2>
...
FREE SPACE (ALLOCATED, BUT UNUSED)
...
END OF PDS
在接下来的几段中,请记住,您可以打开整个 PDS/PDSE,这使您可以读取/写入您喜欢的任何成员,或者您可以分配和打开单个成员,然后处理像任何其他顺序文件一样。
首先,如果您确实要按照问题中显示的那样编码 DD 语句,那么您可能只需将 open 从 fopen(dsn,...) 更改为 fopen(dd:ddname,...)。如果您在 UNIX Shell 下运行,或者您执行的操作导致您的进程在不同的地址空间中运行(例如 fork()),那么这可能不起作用,但值得一试。如果您使用显示的 JCL 执行此操作,挑战将是管理 PDS/E 目录 - 当您创建/更新新成员时,您需要发出自己的“STOW”,因为 JCL 分配整个数据集,而不仅仅是单个成员。顺序是:
- 打开 DD 进行输出。
- 写入您的数据。
- 使用新的成员信息更新 PDS 或 PDS/E 目录(这是 STOW 功能的用武之地 - 它更新 PDS/PDSE 的目录以反映您创建或更新的成员)。
- 关闭文件
如果您还需要读取成员,则需要发出 FIND(或 BLDL/POINT - 在 C 中可以是 fseek())以指向正确的成员,然后读取该成员。我敢肯定这听起来很麻烦,但这种方法的优点是您可以分配/打开文件一次,并根据需要处理任意数量的单个成员。
第二种解决方法可能是自己动态分配文件,然后使用DD:ddname 语法打开它...如果您只是不经常访问该文件,这可能更容易编码。动态分配的血淋淋的细节在这里完整描述:https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.ieaa800/reqsvc.htm。
有几种方法可以调用动态分配:可以编写一个小型汇编程序,可以使用 z/OS UNIX Services BPXWDYN 可调用服务,或者可以使用 C 运行时“dynalloc()”或“svc99() “ 职能。 dynalloc() 函数易于使用,但它只公开了动态分配可以做什么的一个子集……svc99() 使用起来更麻烦,但它公开了更多功能。
无论如何,动态分配采用“文本单元”,这些单元大致对应于您在 JCL DD 语句中找到的参数。你所描述的听起来你只需要传递 DSN 和 DISP 文本单元,也许还有 DDNAME(你可以传递你自己的 DDNAME,或者让系统为你生成一个)。
C 运行时函数使这一切变得简单,但请注意有一些奇怪之处,例如需要将参数填充到最大长度。例如,DSN 需要 44 个字符并在右侧用空格填充 - 而不是 C 样式的以空字符结尾的字符串。
这里以一个小代码sn-p为例:
#include <dynit.h>
. . .
int allocate(ddn, dsn, mem)
{
__dyn_t ip; // Parameters to dynalloc()
. . .
// Prepare the parameters to dynalloc()
dyninit(&ip); // Initialize the parameters
ip.__ddname = ddn; // 8-char blank-padded
ip.__dsname = dsn; // 44-char blank-padded
ip.__status = __DISP_SHR; // DISP=(SHR)
ip.__normdisp = __DISP_KEEP; // DISP=(...,KEEP)
ip.__misc_flags = __CLOSE; // FREE=CLOSE
if (*mem) // Optional PDS, PDS/E member
ip.__member = mem; // 8-char blank-padded
// Now we can call dynalloc()...
if (dynalloc(&ip)) // 0: Success, else error
{
// On error, the errcode/infocode explain why - values
// are detailed in z/OS Authorized Services Reference
printf("SVC99: Can't allocate %s - RC 0x%x, Info 0x%x\n",
dsn, ip.__errcode, ip.__infocode);
return FALSE;
}
// If dynalloc works, you can open the file with fopen("DD:ddname",...)
}
不要忘记,当您处理完文件后,通常需要释放它。
上面的代码 sn-p 使用“FREE=CLOSE”——这意味着当文件关闭时,z/OS 将自动释放分配……如果您只打开并处理一次数据集,这是一种方便的方法。如果您需要反复打开和关闭文件,那么您不会使用 FREE=CLOSE,而是在您完成处理并想要释放文件后再次调用动态分配。
如果您需要同时访问多个文件,请注意您需要生成多个唯一的 DDNAME。您可以在自己的代码中执行此操作,也可以使用动态分配的形式自动构建并返回可用的 DDNAME(形式为“SYSnnnnn”)。
此外,请不要忘记,在某些情况下更新 DISP=SHR 下的数据集可能很危险,尤其是当涉及的数据集可以是传统 PDS 以及 PDS/E 时。最大的危险是两个应用程序同时打开数据集进行输出......两者都会将数据写入同一个地方,结果很可能是损坏的 PDS 目录。
在 UNIX 服务环境中还有一些其他奇怪的地方,特别是如果您使用 fork() 或 exec() 并期望文件句柄在子进程中工作,因为分配通常与特定的 z/OS 地址空间相关联。像 spawn() 这样的服务可以让子进程运行在同一个地址空间,所以这是一种可能。