是否有办法(在C中,或最好在Perl中)确定是否可以写入命名管道 - 即有一个有效的读取过程 似乎如果我打开写入非阻塞,open会立即返回,但select for write也会立即返回。 目标是如果读取结束没有准备好,写入过程只是继续(即跳过发送)
答案 0 :(得分:5)
您可能无法关注open
的返回代码。如果打开FIFO以将和写为非阻塞,则有两种可能的结果。
如果已有读卡器open
将立即成功返回。
如果没有管道阅读器,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以便自己阅读 - 但这有其自身的问题。
如果在select
语句中使用FIFO写入fd,它总是可写的......直到它不可写。也就是说,因为您也是自己的读者,所以FIFO写入将成功,直到您使用PIPE_BUF字节填充FIFO缓冲区。之后,FIFO将无法写入,并且您的写入将在EAGAIN中失败,直到合法的读者(即不是您自己)出现,打开以进行读取,并开始耗尽缓冲区。
通过打开自己的FIFO进行读写,合法用户在关闭FIFO时不会看到EOF。由于您只关心写作,这对您来说可能不是问题。
我从未尝试过的另一种可能的解决方案是在FIFO上使用 inotify 。您可以监视select
中的inotify文件描述符,并确定有人打开FIFO的时间。然后你知道你可以安全地打开你的写作结束。您可能希望屏蔽FIFO上的开放权限,以便您可以成为唯一的编写者,如果可能的话,您的应用程序。
对于这些时髦的语义,应该将FIFO重命名为PITA。如果你能做出改变,这就是为什么Unix众神赋予我们凡人的域名套接字。
答案 1 :(得分:2)
打开管道的写入侧将阻止,直到阅读器打开读取侧,无论其他任何设置如何。因此,在管道连接之前,您无法从open
返回。
如果阅读器关闭已打开管道的一侧,则编写器将在下次写入尝试时收到E_PIPE。据我所知,这是唯一一个指示管道不再连接的指示。
答案 2 :(得分:0)