4

以下小 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在第一个字节之后读取 0 个字节,write()并且(错误地)决定pointless已将其退出。

在没有 的情况下运行pointlessrlwrap 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 的从属端 ing 0 字节是无操作的,而在 AIX 上它唤醒 select()主端的。写入 0 字节似乎毫无意义,但我的一个测试程序写入随机长度的文本块,实际上可能恰好有长度 0。

在 linux 上,man 2 read状态“成功时,返回读取的字节数(零表示文件结束) ”(斜体是我的)这个问题之前已经出现过, 没有提到这种情况。

这就引出了一个问题:我怎样才能便携地确定从端是否已经关闭?(在这种情况下,我可能只是等待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 世界的其他部分。可悲的是,这必须从从属端调用,因此在rlwrap影响范围之外(即使我们可以调用ioctl()中间fork()exec()- 这并不能保证执行的命令不会将其改回)

4

2 回答 2

2

linux 手册页

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

因此,由于未指定,它可以根据您的情况做任何事情。

于 2021-02-09T11:22:29.500 回答
2

每个 POSIX

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

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

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

侧面(加粗我的)write()

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

不幸的是,这意味着您不能可移植地依赖write()零字节的 a 无效,因为 AIX 与write()此处的 POSIX 标准兼容。

你可能不得不依赖SIGCHLD.

于 2021-02-09T11:29:55.667 回答