【问题标题】:reading from named pipes in linux vs OS X从 linux vs OS X 中的命名管道读取
【发布时间】:2014-01-24 13:19:33
【问题描述】:

下面的程序在 OS X 下运行良好,但在 linux 下不行。它继续循环通过 perror("read error") 行,读取缓冲区中没有字节,并且 EWOULDBLOCK 不是 errno (errno=0);

在 OS X 中,程序按预期工作,即它从三个命名管道中读取数据,并将其中任何一个管道中的任何数据打印到控制台。

        #include <sys/types.h>
        #include <sys/select.h>
        #include <sys/time.h>
        #include <sys/types.h>
        #include <errno.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include <fcntl.h>
        #include <unistd.h>


        int readPipe(int fd)
        {
            ssize_t bytes;
            size_t total_bytes = 0;
            char buffer[100*1024];

            printf("\nReading pipe descriptor # %d\n",fd);
            for(;;) {
                bytes = read(fd, buffer, sizeof(buffer));
                if (bytes > 0) {
                    total_bytes += (size_t)bytes;
                    printf("%s", buffer);
                } 
                else {
                    if (errno == EWOULDBLOCK) {
                        break; // recieve buffer is empty so return to main loop
                    } 
                    else {
                        perror("read error"); 
                        return EXIT_FAILURE;
                    }
                }
            }
            return EXIT_SUCCESS;
        }


        int main(int argc, char* argv[])
        {
            int fd_a, fd_b, fd_c;   // file descriptors for each pipe
            int nfd;                // select() return value
            fd_set read_fds;        // file descriptor read flags
            struct timeval tv;
            tv.tv_sec = 0;
            tv.tv_usec = 10000;

            // create pipes to monitor (if they don't already exist)
            system("mkfifo /tmp/PIPE_A");
            system("mkfifo /tmp/PIPE_B");
            system("mkfifo /tmp/PIPE_C");
            system("chmod 666 /tmp/PIPE_*");

            // open file descriptors of named pipes to watch
            fd_a = open("/tmp/PIPE_A", O_RDONLY | O_NONBLOCK); // the O_RDWR flag is undefined on a FIFO.
            if (fd_a == -1) {
                perror("open error");
                return EXIT_FAILURE;
            }

            fd_b = open("/tmp/PIPE_B", O_RDONLY | O_NONBLOCK);
            if (fd_b == -1) {
                perror("open error");
                return EXIT_FAILURE;
            }

            fd_c = open("/tmp/PIPE_C", O_RDONLY | O_NONBLOCK);
            if (fd_c == -1) {
                perror("open error");
                return EXIT_FAILURE;
            }

            // check for new data in each of the pipes
            for(;;)
            {
                // clear fds read flags
                FD_ZERO(&read_fds);

                // PIPE_A
                FD_SET(fd_a, &read_fds);
                nfd = select(fd_a+1, &read_fds, NULL, NULL, &tv);
                if (nfd != 0) {
                    if (nfd == -1) {
                        perror("select error");
                        return EXIT_FAILURE;
                    }
                    if (FD_ISSET(fd_a, &read_fds)) {
                        readPipe(fd_a);
                    }
                }

                // PIPE_B
                FD_SET(fd_b, &read_fds);
                nfd = select(fd_b+1, &read_fds, NULL, NULL, &tv);
                if (nfd != 0) {
                    if (nfd == -1) {
                        perror("select error");
                        return EXIT_FAILURE;
                    }
                    if (FD_ISSET(fd_b, &read_fds)){
                        readPipe(fd_b);
                    }
                }

                // PIPE_C
                FD_SET(fd_c, &read_fds);
                nfd = select(fd_c+1, &read_fds, NULL, NULL, &tv);
                if (nfd != 0) {
                    if (nfd == -1) {
                        perror("select error");
                        return EXIT_FAILURE;
                    }
                    if (FD_ISSET(fd_c, &read_fds)){
                        readPipe(fd_c);
                    }
                }
            }
            return EXIT_SUCCESS;
        }

【问题讨论】:

  • 管道读取 (tv.tv_usec) 的超时时间应为 0(对于两个操作系统)。

标签: c linux macos posix named-pipes


【解决方案1】:

read 可以返回 0 是允许的(也是预期的)。这意味着管道正在返回 EOF。你没有处理这种情况。 errno 的内容无关紧要,除非调用失败并返回 -1。

for (;;)
{
    bytes = read(fd, buffer, sizeof(buffer));

    if (bytes > 0)
    {
        total_bytes += (size_t)bytes;
        printf("%s", buffer);
    }

    if (bytes == 0)
        return //something appropriate

    if (bytes == -1)
    {
        if (errno == EWOULDBLOCK)
           break; // recieve buffer is empty so return to main loop
        else
        {
            perror("read error");
            return EXIT_FAILURE;
        }
    }
}

您正在努力返回不同的代码,但您在 main 中没有注意到它们。

另外,这 3 个 select 语句又是怎么回事?我认为这已在上一个问题中得到解决。

编辑

for (;;)
{
    // clear fds read flags
    FD_ZERO(&read_fds);
    FD_SET(fd_a, &read_fds);
    FD_SET(fd_b, &read_fds);
    FD_SET(fd_c, &read_fds);

    tv.tv_sec = 0;
    tv.tv_usec = 10000;

    nfd = select(fd_c + 1, &read_fds, NULL, NULL, &tv);

    if (nfd == 0) //timeout - continue or do something else for a bit
        continue;

    if (nfd == -1)
    {
        perror("select error");
        return EXIT_FAILURE;
    }

    if (FD_ISSET(fd_a, &read_fds))
        readPipe(fd_a);

    if (FD_ISSET(fd_b, &read_fds))
        readPipe(fd_b);

    if (FD_ISSET(fd_c, &read_fds))
        readPipe(fd_c);
}

【讨论】:

  • 感谢您的反馈。当 errno = 0 时,正在读取的管道没有数据,这是预期的情况,因此 EXIT_SUCCESS 是合适的(我意识到在这个小示例程序中没有处理)。在 linux 中应用此更改,它仍然循环通过 readPipe 调用(但不是在 OS X 中)。因此,如果我在 linux 下删除 printf() 语句,它的表现会好得多。关于三个选择,我减少了 fd_sets 但我仍然需要多个选择(我还没有找到我正在尝试做的模型或示例)。如果你能指点我一个,那将不胜感激!。
  • 不,这不太对。如果 return code 为 0,则没有数据 - 实际上管道的 writer 端已关闭。除非函数返回 -1,否则 Errno 是没有意义的。大多数调用设置 errno,除非调用失败。因此,在没有失败调用的情况下,errno 中的任何内容都可能来自对任何先前文件描述符的任何先前调用。我基本上已经概述了你想用 select 做什么。
  • 我看到 FD_ISSET 只有在所有三个 fd 都用于更新 read_fds 标志之后才被调用,然后最高 fd 用于选择。看起来不错,效率也更高。我也明白 errno=0 意味着 EOF(作家关闭)以及现在如何处理。我仍然不清楚 OS X 和 Linux 之间的不同行为,但我只需要 linux。看起来 read_fds 标志可能会在读取发生后在操作系统之间以不同方式设置/清除,或者类似的事情......
  • @Wait,我要第三次这么说,以确保我们在同一页面上。 "errno == 0" not 表示 EOF。等于 0 的返回码(在您的情况下为“字节”)是 EOF。 errno 与失败(返回 -1)调用有关。 read/write/exc 集由 select 更改 - 这就是您知道它们是可读/可写的方式 - 因此需要在每次调用之前重置。这是 posix 兼容的。您可能在 OSX 上没有注意到它,因为您每次调用只使用了一个 FD(因此其他两个不可能更改)。
  • 在 linux 上,计时器字段也应该在每次调用之前重置。我忘了这是否由 posix 强制执行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多