如何判断管道是否可写

问题描述 投票:0回答:3

有没有一种方法(在 C 中,或者最好是在 Perl 中)来确定是否可以写入命名管道 - 即有一个主动的读取过程 看来,如果我以非阻塞方式打开,打开会立即返回,但写入的选择也会立即返回。 目标是如果读取端未准备好,则写入过程继续进行(即跳过发送)

linux perl posix named-pipes
3个回答
6
投票

您可能没有注意

open
的返回代码。 如果您打开 FIFO 以非阻塞方式写入 ,则有两种可能的结果。

  1. 如果已经有读者,

    open
    将立即成功返回。

  2. 如果没有管道读取器,

    open
    将失败,并显示 errno = ENXIO。

以下程序演示。

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/stat.h>

#define SERVFIFO "/tmp/server.fifo"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)

void syserr(const char *str)
{
    perror(str);
    exit(1);
}

int main(int argc, char** argv)
{
    umask(0);

    if (mkfifo(SERVFIFO, FILE_MODE) < 0 && errno != EEXIST)
        syserr("mkfifo");

    // try to open for write with no readers

    int fdw = open(SERVFIFO, O_WRONLY | O_NONBLOCK);
    if (fdw == -1)
        perror("non-blocking open for write with no readers failed");

    // create a reader - the process itself - non-blocking

    int fdr = open(SERVFIFO, O_RDONLY | O_NONBLOCK);
    if (fdr == -1)
        syserr("non-blocking open for read no writers failed");

    // try again to open for write but this time with a reader
    fdw = open(SERVFIFO, O_WRONLY | O_NONBLOCK);
    if (fdw == -1)
        syserr("non-blocking open with readers failed");

    printf("non-blocking open for write succeeded\n");

    close(fdw);
    close(fdr);
    unlink(SERVFIFO);
}

也就是说,

open
在没有阻塞的情况下失败的事实并没有那么有用。 您唯一能做的就是不断尝试打开,直到成功为止,这种轮询可能是浪费,并且在长时间运行的程序等待间歇性读者的情况下有点荒谬。

一种解决方案是上面使用的 - 打开 FIFO 来自己读取 - 但这有其自身的问题。

  1. 如果您在

    select
    语句中使用 FIFO write fd,它始终是可写的......直到它不可写为止。 也就是说,因为您也是自己的读取器,所以 FIFO 写入将会成功,直到您用 PIPE_BUF 字节填充 FIFO 缓冲区。 之后,FIFO 将不可写入,并且您的写入将失败并显示 EAGAIN,直到出现合法读取器(即不是您自己),打开读取并开始耗尽缓冲区。

  2. 通过打开自己的 FIFO 进行读写,当合法用户关闭 FIFO 时,您将不会看到来自 EOF 的信息。 由于您只关心写作,这对您来说可能不是问题。

另一个可能的解决方案,我从未尝试过,是在 FIFO 上使用 inotify。 您可以监视

select
中的 inotify 文件描述符并确定何时有人打开了 FIFO。 然后你就知道你可以安全地开始写作了。 如果可能的话,您可能希望屏蔽 FIFO 上的开放权限,以便您成为应用程序的唯一写入者。

为了这些时髦的语义,FIFO 应该被重命名为 PITA。 如果你能做出改变,这就是 Unix 诸神将域套接字授予我们凡人的原因。


2
投票

无论任何其他设置如何,打开管道的写入侧都会阻塞,直到读取器打开读取侧。 因此,在管道连接之前,您无法从

open
返回。

如果读取器关闭已打开管道的一侧,则写入器将在下一次尝试写入时收到 E_PIPE。 据我所知,这是管道不再连接的唯一指示。


0
投票

看起来 - 作者没有机会检查,这个策略会起作用: 打开非阻塞,然后只写 - 无论打开后是否将模式更改为阻塞,写入似乎都会成功(因此,如果读者实际上没有等待,则写入者将继续)

© www.soinside.com 2019 - 2024. All rights reserved.