如何在 fopen 上使用 DISP=SHR

Posted

技术标签:

【中文标题】如何在 fopen 上使用 DISP=SHR【英文标题】:How to use DISP=SHR on an fopen 【发布时间】:2020-05-29 07:19:04 【问题描述】:

代码如下:

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

【问题讨论】:

我希望能为您找到更好的答案,但到目前为止,我被告知您不能,您应该提出增强请求。 谢谢@KevinMcKenzie 我会这样做的。您是否知道这应该是 C 编译器上的 RFE,还是“z Systems Software”列表中的某个东西,如果是这样呢? fopen() 是一个 C 运行时库函数,所以 RFE 应该在语言环境中。 RFE 提出 - 见 ibm.com/developerworks/rfe/… @ValerieR - 我确实希望有人用引用 dynalloc() 的东西来回答,因为我已经看到了,并且想知道这是否是我应该使用的。当没有人这样做时,我认为我误解了关于 dynalloc() 所做的稀疏的手册细节。如果你把它写下来作为答案,我会接受。 【参考方案1】:

有两种可能……

对于不熟悉分区数据集(即 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() 这样的服务可以让子进程运行在同一个地址空间,所以这是一种可能。

【讨论】:

STOW 是什么意思?注:您回答的第一段是我问题的工作部分。当您没有来自 JCL 的 DD 名称,而只有一个完全限定的 PDSE 目录名称时,就会出现问题。 用更多信息更新了我的答案...添加/更新 PDS/PDSE 成员需要更改目录以反映您编写的成员,STOW 是执行此操作的系统服务。分配 DSN(MEMBER) 时,如果您有很多成员需要处理,系统会自动为您分配文件,代价是一遍又一遍地分配文件。我不知道 C 运行时中的 STOW 函数......如果你这样做可能需要几行汇编代码,所以如果你可以摆脱动态分配方法,它可能需要更少的代码和没有汇编程序。 谢谢。我不需要使用 STOW - 一个成员的 fopen 似乎可以为你做这一切。

以上是关于如何在 fopen 上使用 DISP=SHR的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SORT 操作中减少 CPU

F2的vb文件的JOINKEYS REFORMAT字段无效

如何查看我在文件上使用了多少次 fopen?

汇编 8086 中的 SHR 命令

matlab中的disp函数和num2str()函数如何使用。

php中fopen()如何理解?