【问题标题】:How to see if a pipe is empty如何查看管道是否为空
【发布时间】:2012-11-28 11:53:59
【问题描述】:

假设一个管道,

int pipe_fd[2];
pipe(pipe_fd);

我们分叉,并期望一个进程将在任意时间写入管道。在其中一个过程中,我们希望能够在不阻塞的情况下检查管道的内容。

即如果没有任何内容并且写入端保持打开状态,则典型的读取将阻塞。我想去做其他的事情,甚至可能一次读一点,做一些事情,然后再回来看看是否还有更多,唉:

close(pipe_fd[1]);

while(1){
    if(/**Check pipe contents**/){
        int present_chars = 0;    

        while( read(pipe_fd[0],&buffer[present_chars],1) != 0)
            ++present_chars;

        //do something

    }
    else
        //do something else
}

【问题讨论】:

  • 由于pipe()函数不允许你设置非阻塞模式,你必须在使用fcntl()F_GETFLF_SETFL加上@987654329创建管道之后这样做@.
  • 注意Linux也有pipe2函数,允许你在创建管道的同时请求非阻塞模式。这个函数还不是标准的,但已经被接受包含在下一个版本的 POSIX 中,因为在创建管道的同时原子地设置 close-on-exec 标志也是必要的。
  • @R.. 还没有标准化..
  • @snr: 下一期还没有发布。接受的文本在错误跟踪器中:austingroupbugs.net/view.php?id=411

标签: c block pipe blocking


【解决方案1】:

R.. 的答案很好,但是 poll 返回在“revents”中设置了标志的文件描述符结构的数量。如果您可以从fd 读取,则该值为 1,但如果设置了任何错误标志,则该值为 1。这意味着 R.. 的回答会说管道在进入错误状态时是可读的。更强大的检查可能是这样的:

bool canReadFromPipe(){
    //file descriptor struct to check if POLLIN bit will be set
    //fd is the file descriptor of the pipe
    struct pollfd fds{ .fd = fd, .events = POLLIN };
    //poll with no wait time
    int res = poll(&fds, 1, 0);

    //if res < 0 then an error occurred with poll
    //POLLERR is set for some other errors
    //POLLNVAL is set if the pipe is closed
    if(res < 0||fds.revents&(POLLERR|POLLNVAL))
    {
        //an error occurred, check errno
    }
    return fds.revents&POLLIN;
}

【讨论】:

    【解决方案2】:

    你的逻辑是错误的,read 在字符用完时不会返回 0;相反,它将阻塞直到接收到更多,除非您将文件置于非阻塞模式,但随后它将返回-1并将errno设置为EWOULDBLOCKEAGAIN而不是返回0。唯一一次@ 987654326@ can ever return 0 是当 size 参数为 0 或到达文件结尾时。并且,对于管道,end-of-file表示管道的写入端已经关闭;文件结束状态不会仅仅因为还没有任何可用的输入而发生。

    话虽如此,最简单的检查方法是:

    if (poll(&(struct pollfd){ .fd = fd, .events = POLLIN }, 1, 0)==1) {
        /* data available */
    }
    

    但除非您使用非阻塞模式,否则您需要在每次读取操作之前进行此检查。将更大的缓冲区传递给read,而不是一次一个字节地进行,将消除大部分检查成本。

    【讨论】:

    • 根据read(3),“如果[...] O_NONBLOCK 已设置,read() 应[...] 将errno 设置为[EAGAIN]”,而不是EWOULDBLOCK。好吧,除非我们使用它的不同实现。
    • “正确”值为EWOULDBLOCK。现代 POSIX 允许两者之一,并允许两个错误宏具有相同的值,这在 Linux 上是这样做的。参见pubs.opengroup.org/onlinepubs/9699919799/functions/read.html 严格来说,可移植应用程序应该测试errno==EAGAIN || errno==EWOULDBLOCK
    • 哦,好吧。我没有意识到这一点。然而它也写在我的manfile中......谢谢:)
    • 是的,你是对的。我也会这样做。我投票给你,因为你显然对这个话题更了解。 ;)
    • 你的也一样,因为你实际上介绍了如何使用非阻塞模式。
    【解决方案3】:

    您可以使用read() 函数检查是否有要读取的数据。来自read(3)

    When attempting to read from an empty pipe or FIFO:
    
    * If some process has the pipe open for writing and
    O_NONBLOCK is set, read() shall return -1 and set
    errno to [EAGAIN].
    
    * If some process has the pipe open for writing and
    O_NONBLOCK  is  clear,  read() shall block the calling
    thread until some data is written or the pipe is
    closed by all processes that had the pipe open for
    writing.
    
    The read() function shall fail if:
    
    EAGAIN or EWOULDBLOCK
    
        The file descriptor is for a socket, is marked
        O_NONBLOCK, and no data is waiting to be received.
    

    因此,如果您设置O_NONBLOCK,您将能够通过简单地调用read() 来判断是否要在管道上读取某些内容。

    提醒一下,来自open(3)

    SYNOPSIS
        int open(const char *path, int oflag, ...  );
    
    DESCRIPTION
        Values for oflag are constructed by a
        bitwise-inclusive OR of flags from the following
        list, defined in <fcntl.h>. Applications shall
        specify exactly one  of  the first three values
        (file access modes) below in the value of oflag:
    
        O_NONBLOCK [...]
    

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 2016-03-26
      • 1970-01-01
      • 1970-01-01
      • 2022-12-16
      • 1970-01-01
      • 2014-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多