【问题标题】: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 现在会阻塞。但是,我不知道该怎么做。

我相信你的集体智慧 :) 标记。

【问题讨论】:

    标签: c select named-pipes nonblocking


    【解决方案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 * 被交给您的方式,您既不能释放()它也不能删除它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-25
    • 1970-01-01
    • 2020-07-18
    • 1970-01-01
    • 2010-11-07
    相关资源
    最近更新 更多