有没有办法(在C中,或最好在Perl中)确定是否可以写入命名管道 – 即有一个有效的读取过程
似乎如果我打开写入非阻塞,open会立即返回,但select for write也会立即返回.
目标是如果读取结束尚未准备好,则写入过程仅进行(即跳过发送)
似乎如果我打开写入非阻塞,open会立即返回,但select for write也会立即返回.
目标是如果读取结束尚未准备好,则写入过程仅进行(即跳过发送)
解决方法
有可能你没有注意打开的返回码.如果打开FIFO进行写入和非阻塞,则有两种可能的结果.
>如果已有读卡器,则打开将立即成功返回.
>如果没有管道读取器,则打开将失败并显示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.您可以在选择中监视inotify文件描述符,并确定何时有人打开FIFO.然后你知道你可以安全地开始写作.您可能希望屏蔽FIFO上的打开权限,以便您可以成为唯一的编写者,如果可能的话,您的应用程序.
对于这些时髦的语义,FIFO应该被重命名为PITA.如果你能做出改变,这就是为什么Unix众神赋予我们凡人的域名套接字.