如何在 fopen 上使用 DISP=SHR

How to use DISP=SHR on an fopen

代码如下:

fopen("DD:LOGLIBY(L1234567)", "w");

JCL 喜欢:

//LOGTEST  EXEC PGM=LOGTEST
//LOGLIBY  DD   DSN=MYUSER.LOG.LIBY,DISP=SHR

我可以创建 PDS(E) 成员,同时浏览 PDS(E) 以查看现有成员,正如 DISP=SHR 所预期的那样。

如果我改为编码:

fopen("//'MYUSER.LOG.LIBY(L1234567)'", "w");

如果我当时正在浏览PDS(E) fopen失败,或者当我打开文件时浏览PDS(E)失败。也就是说没有DISP=SHR。根据 fopen() 文档,DISP=SHR 是使用 "r" 等文件模式时的默认值,但不是 "w".

如何在第二个例子中提供DISP=SHR

有两种可能...

对于不熟悉分区数据集(即 PDS 或 PDS/E)内部结构的任何人,这些数据集在逻辑上分为两部分:"directory" 具有指向所有个体的指针成员,以及包含各个成员实际记录的 "data" 区域:

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,这样您就可以 read/write 任何您喜欢的成员,或者您可以分配并打开一个成员,并且像任何其他顺序文件一样得到处理。

首先,如果您实际上要按照问题中所示的那样对 DD 语句进行编码,那么您可能只需要将打开方式从 fopen(dsn,...) 更改为 fopen(dd:ddname,...)。如果您 运行ning 在 UNIX Shell 下,或者您执行的操作导致进程 运行ning 在不同的地址 space(例如 fork()),那么这可能行不通,但值得一试。如果您使用显示的 JCL 执行此操作,挑战将是管理 PDS/E 目录 - 当您 create/update 一个新成员时,您需要发布自己的 "STOW",因为 JCL 分配整个数据集,而不仅仅是单个成员。顺序为:

  1. 打开DD输出。
  2. 写下你的数据。
  3. 使用新成员信息更新 PDS 或 PDS/E 目录(这是 STOW 函数的用武之地 - 它更新 PDS/PDSE 的目录以反映您创建或更新的成员)。
  4. 关闭文件

如果您还需要读取成员,则需要发出 FIND(或 BLDL/POINT - 在 C 中可以是 fseek())以指向正确的成员,然后读取该成员。我敢肯定这听起来很麻烦,但这种方法的优点是您可以 allocate/open 文件一次,并根据需要处理任意数量的单个成员。

第二种解决方法可能是自己动态分配文件,然后使用 DD:ddname 语法打开它...如果您只是不经常访问该文件,这可能更容易编写代码。动态分配的血淋淋的细节在这里得到了完整的描述:https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.ieaa800/reqsvc.htm.

有几种调用动态分配的方法:您可以编写一个小的汇编程序,您可以使用 z/OS UNIX 服务 BPXWDYN 可调用服务,或者您可以使用 C 运行time "dynalloc()" 或 "svc99()" 函数。 dynalloc() 函数易于使用,但它只公开了动态分配可以做的事情的一个子集...svc99() 使用起来更麻烦,但它公开了更多的功能。

无论您如何操作,动态分配都会采用 "text units",这大致对应于您在 JCL DD 语句中找到的参数。你所描述的听起来像是你只需要传递 DSN 和 DISP 文本单元,也许还有 DDNAME(你可以传递你自己的 DDNAME,或者让系统为你生成一个)。

C 运行time 函数使这一切变得简单,但要注意有一些奇怪的地方,例如需要将参数填充到它们的最大长度。例如,DSN 需要为 44 个字符,并在右侧用空格填充——而不是 C 风格的以 null 结尾的字符串。

这里有一个小代码片段作为示例:

#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",...)

}

不要忘记,当您使用完文件后,通常需要解除分配。 上面的代码片段使用 "FREE=CLOSE" - 这意味着当文件关闭时,z/OS 将自动释放分配...如果您只打开并处理数据集一次,这是一种方便的方法。如果您需要重复打开和关闭文件,则不会使用 FREE=CLOSE,而是在完成处理并想释放文件后再次调用动态分配。

如果您需要同时访问多个文件,请注意您需要生成多个唯一的 DDNAME。您可以在自己的代码中执行此操作,也可以使用自动构建的动态分配形式和 returns 可用的 DDNAME(形式为 "SYSnnnnn")。

此外,不要忘记在 DISP=SHR 下更新数据集在某些情况下可能很危险,尤其是当涉及的数据集既可以是传统 PDS 也可以是 PDS/E 时。最大的危险是两个应用程序同时打开数据集进行输出...两者都会将数据写入同一个地方,结果很可能是损坏的 PDS 目录。

UNIX 服务环境中还有其他一些奇怪的地方,特别是如果您使用 fork() 或 exec() 并期望文件句柄在子进程中工作,因为分配通常与特定的 z/OS 地址相关space。像spawn()这样的服务可以让子进程运行在同一个地址space,所以这是一种可能。