当作者来来去去时从命名管道重新读取
Posted
技术标签:
【中文标题】当作者来来去去时从命名管道重新读取【英文标题】:Re-reading from a named pipe when writers come and go 【发布时间】:2010-01-19 09:01:04 【问题描述】:我遇到了一个问题,我必须从命名管道中读取。我必须处理命名管道的写入者来来去去的情况,但我需要在整个应用程序中保持同一个管道打开。
我在下面的代码中总结了这一点。
int main( int c, char *v[] )
int rfd;
if ( (rfd = open( PIPENAME, O_RDONLY | O_NONBLOCK )) < 0 )
perror( "open" );
return 1;
char buffer[ 1024000 ];
// used to give select an upper bound on number of fds
int nfd = rfd + 1;
fd_set rfds;
FD_ZERO( &rfds );
FD_SET( rfd, &rfds );
while( true )
int nr = select( nfd, &rfds, NULL, NULL, NULL );
if ( nr < 0 )
perror( "select" );
break;
if ( FD_ISSET( rfd, &rfds ) )
//std::cout << "RFD SET" << std::endl;
// Ok, we have data we can read
int nread = read( rfd, buffer, sizeof( buffer ) );
if ( nread < 0 )
perror( "read" );
break;
else if ( nread == 0 )
std::cout << "read 0" << std::endl;
else
std::cout << "read " << nread << " bytes" << std::endl;
close( rfd );
return 0;
我遇到的问题是,在第一个进程写入命名管道并且断开(关闭)它结束后,我的程序不会阻塞选择。它有效地设置了 rfd,并且读取返回在紧密循环中读取的零字节。
我需要 rfd 处于 NON_BLOCKING 模式,否则打开会阻塞,直到出现写入器。
我尝试使用 fcntl 设置为 BLOCKING 模式,但这也不起作用。
我对管道语义的有限理解使我认为我需要清除管道上的 EOF 状态,以便 select 现在会阻塞。但是,我不知道该怎么做。
我相信你的集体智慧 :) 标记。
【问题讨论】:
【参考方案1】:好的,我想出了一个解决方案,但我对此并不满意。如果你问我,这有点像“敲碎坚果的锤子”。
.....
else if ( nread == 0 )
std::cout << "read 0" << std::endl;
FD_ZERO( &rfds );
close( rfd );
if ( (rfd = open( PIPENAME, O_RDONLY | O_NONBLOCK )) < 0 )
perror( "re-open" );
break;
FD_SET( rfd, &rfds );
nfd = std::max( wfd, rfd ) + 1;
else
.....
基本上,我关闭并重新打开管道。
我仍然欢迎更好的解决方案。
【讨论】:
如果您想继续使用命名管道,则关闭并重新打开管道是正确且唯一的解决方案。不过,听起来 UNIX 域套接字实际上可能更适合您的应用程序(它们也被文件系统中的路径名引用,但使用 BSD 套接字 API)。 我尝试使用 unix 套接字,但您不能简单地“cat 文件名 > named_pipe”。您必须编写一个专用应用程序以“套接字”方式格式化应用程序的输入【参考方案2】:在其他帖子中找到:
if ( (rfd = open( PIPENAME, O_RDWR | O_NONBLOCK )) < 0 )
即打开 RDWR 而不是 RDONLY 似乎可行...
【讨论】:
只是这个选项不尊重“数据包”边界(我不知道 RDONLY 是否这样做。即一个人可能会一次读取多个写入 - 或者例如 1.5 写入以防读取大小是写入数据包大小的约 1.5...【参考方案3】:您是否尝试过以非阻塞模式打开 fd,当您的 read()
返回 EWOULDBLOCK
/EAGAIN
时,在 fd 上执行 clearerr()
?
【讨论】:
是的。我能看到的唯一方法是fdopen
为 fd 获取 FILE *
,然后在上面使用 clearerr()
。
问题是你永远不能关闭 FILE * 因为那会关闭底层的 fd。我上面的示例假设我已经打开了管道,但实际上我希望解决方案即使交给管道 fd 也能正常工作。最终在这种情况下,使用 fdopen/clearerr 会导致内存泄漏,因为您不知道 FILE * 被交给您的方式,您既不能释放()它也不能删除它。以上是关于当作者来来去去时从命名管道重新读取的主要内容,如果未能解决你的问题,请参考以下文章
shell 命名管道,进程间通信, ncat作http server