当作者来来去去时从命名管道重新读取

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 * 被交给您的方式,您既不能释放()它也不能删除它。

以上是关于当作者来来去去时从命名管道重新读取的主要内容,如果未能解决你的问题,请参考以下文章

SIGPIPE 如何影响命名管道中的作者?

shell 命名管道,进程间通信, ncat作http server

当阅读器断开连接时,命名管道 (FIFO) 数据会去哪里?

从命名管道读取不会给出任何输出并无限期地阻塞代码

LINUX FIFO 命名管道 (IPC) 在特定文件描述符处停止写入/读取消息

如何继续从命名管道/流中发送/读取消息