微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

read(fd, buf, N>0) == 0,但 fd 不在 EOF 处?

如何解决read(fd, buf, N>0) == 0,但 fd 不在 EOF 处?

下面的小 C 程序(我们称之为 pointless):

/* pointless.c */
#include <stdio.h>
#include <unistd.h>

void main(){
  write(STDOUT_FILENO,"",0); /* pointless write() of 0 bytes */
  sleep(1);
  write(STDOUT_FILENO,"still there!\n",13);
}

将打印“还在那里!”正如预期的那样,经过一小段延迟。然而, rlwrap ./pointless 在 AIX 下不打印任何内容并立即退出

显然,rlwrap 在第一个 write() 之后读取了 0 个字节,并且 (错误地)决定 pointless调用退出

在没有 pointless 的情况下运行 rlwrap 并且 with rlwrap 时 我可以使用的其他系统(Linux、OSX、FreeBSD),“仍然 那里!”按预期打印。

相关的rlwrap(伪)代码是这样的:

/* master is  the file descriptor of the master end of a pty,while the slave is 'pointless's stdout   */
/* master was opened with O_NDELAY                                                                     */
while(pselect(nfds,&readfds,.....)) {
   if (FD_ISSET(master,&readfds)) {           /* master is "ready" for reading       */
      nread = read(master,buf,BUFFSIZE - 1); /* so try to read a buffer's worth     */
      if (nread == 0)                          /* 0 bytes read...                     */
         cleanup_and_exit();                   /* ... usually means EOF,doens't it?  */

显然,在除 AIX 之外的所有系统上,write在 pty 的从属端是空操作,而在 AIX 上它唤醒 select() 在主端。写入 0 个字节似乎毫无意义,但写入 0 个字节 我的测试程序写入随机长度的文本块,这可能 实际上恰好有长度 0。

在 linux 上,man 2 read 表示“成功时,读取的字节数为 返回(零表示文件结束)”(斜体是我的)这个 问题有come up before 没有提到这个场景。

这就引出了一个问题:我如何便携确定 slave端已经关闭? (在这种情况下,我可能只能等待 SIGCHLD 然后关闭商店,但这可能会打开另一罐 我宁愿避免的蠕虫)


编辑:POSIX 状态:

将长度为零的缓冲区(nbyte 为 0)写入 STREAMS 设备会发送 0 字节并返回 0。但是,将零长度缓冲区写入基于 STREAMS 的管道或 FIFO 不会发送任何消息并返回 0。该进程可能会发出 I_SWROPT ioctl() 以启用零长度消息通过管道或 FIFO 发送。

在 AIX 上,pty 确实是一个 STREAMS 设备,而且,不是管道或 FIFO。 ioctl(STDOUT_FILENO,I_SWROPT,0) 似乎可以使 pty 符合 Unix 世界的其余部分。可悲的是,这必须从 slave调用,因此在 rlwrap 的影响范围之外(即使我们可以调用 {{ 1}} 和 ioctl() - 这不能保证执行的命令不会改变它)

解决方法

来自linux man page

如果 count 为零并且 fd 指的是常规文件,则 write() 如果出现以下错误之一,则可能会返回失败状态 检测到。如果未检测到错误,或未检测到错误 执行时,将返回 0,不会造成任何其他影响。 如果 count 为零并且 fd 指的是常规文件以外的文件 文件,未指定结果。

因此,由于它是未指定的,因此它可以在您的情况下为所欲为。

,

Per POSIX

尝试从空管道或 FIFO 读取时:

  • 如果没有进程打开管道进行写入,则 read() 应返回 0 以指示文件结束。"

所以“读取零字节意味着 EOF”是符合 POSIX 的。

the write() side(我的粗体):

在执行下述任何操作之前,如果 nbyte 为零且文件是常规文件,write() 函数可能会检测并返回如下所述的错误。如果没有错误,或者如果没有执行错误检测,write() 函数将返回零并且没有其他结果。 如果 nbyte 为零且文件不是常规文件,则结果未指定。

不幸的是,这意味着您不能可移植地依赖于零字节的 write() 使其无效,因为 AIX 符合此处 write() 的 POSIX 标准。

您可能不得不依赖SIGCHLD

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。