如何在 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 分配整个数据集,而不仅仅是单个成员。顺序为:
- 打开DD输出。
- 写下你的数据。
- 使用新成员信息更新 PDS 或 PDS/E 目录(这是 STOW 函数的用武之地 - 它更新 PDS/PDSE 的目录以反映您创建或更新的成员)。
- 关闭文件
如果您还需要读取成员,则需要发出 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,所以这是一种可能。
代码如下:
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 分配整个数据集,而不仅仅是单个成员。顺序为:
- 打开DD输出。
- 写下你的数据。
- 使用新成员信息更新 PDS 或 PDS/E 目录(这是 STOW 函数的用武之地 - 它更新 PDS/PDSE 的目录以反映您创建或更新的成员)。
- 关闭文件
如果您还需要读取成员,则需要发出 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,所以这是一种可能。