【问题标题】:how to determine if pipe can be written如何确定是否可以写入管道
【发布时间】:2014-01-08 08:16:40
【问题描述】:

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

【问题讨论】:

标签: linux perl posix named-pipes


【解决方案1】:

无论任何其他设置如何,打开管道的写入端都会阻塞,直到读取器打开读取端为止。所以在管道连接之前你不能从open返回。

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

【讨论】:

  • 写入端(以非阻塞方式打开)将返回,但 errno 设置为错误。
【解决方案2】:

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

【讨论】:

  • 写入不成功,您忽略了对未打开文件描述符的写入错误。
【解决方案3】:

您可能没有注意来自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 写入 fd,它将始终是可写的......直到它不是。也就是说,因为您也是自己的读者,所以 FIFO 写入将成功,直到您用 PIPE_BUF 字节填充 FIFO 缓冲区。之后,FIFO 将无法写入,并且您的写入将因 EAGAIN 而失败,直到合法读取器(即不是您自己)出现,打开读取并开始耗尽缓冲区。

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

另一个我从未尝试过的可能解决方案是在您的 FIFO 上使用 inotify。您可以监控 select 中的 inotify 文件描述符,并确定何时有人打开了 FIFO。然后你知道你可以安全地打开你的写作终点。您可能希望屏蔽 FIFO 上的开放权限,以便您可以成为唯一的编写者,如果可能的话,您的应用程序。

对于这些时髦的语义,FIFO 应该重命名为 PITA。如果你能做出改变,这就是为什么 Unix 之神赐予我们这些凡人的域套接字的原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-31
    • 2012-05-06
    • 1970-01-01
    相关资源
    最近更新 更多