【问题标题】:Select on named pipe (FIFO) causes infinite loop在命名管道(FIFO)上选择会导致无限循环
【发布时间】:2018-12-29 19:46:34
【问题描述】:

我有一个循环。在这个循环中,我尝试使用select() 检测是否在命名管道(FIFO)文件上触发了读取或写入。

如果触发读取,我会在 FIFO 文件描述符上调用 read()
如果触发了写入,我会在 FIFO 文件描述符上调用 write()

问题是,如果发生写入并且我写入 FIFO,它将触发读取。然后当我从 FIFO 读取时,它会触发写入。导致无限循环。

如果我在O_RDWR 模式下使用相同的文件描述符,则会立即发生此循环。如果我为读取和写入创建单独的文件描述符,则会在第一次写入之后发生此循环。

#include <errno.h>
#include <sys/select.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <string.h>

int main() {
    // Open export fifo
    int fd = open("./foo-fifo", O_RDWR | O_CREAT);
    if (fd < 0) { // Failed to open
        perror("error opening fifo");
    }

    // Read or write fifo until "quit" is in buffer 
    while (true) {
        fd_set read_fds;
        fd_set write_fds;

        FD_ZERO(&read_fds);
        FD_SET(fd, &read_fds);

        FD_ZERO(&write_fds);
        FD_SET(fd, &write_fds);

        int num_fds = select(fd+1, &read_fds, &write_fds, NULL, NULL);
        if (num_fds < 0) { // Failed to select
            perror("failed to select fifo fd");
        } else if (num_fds == 0) { // Timeout
            continue;
        }

        // If read
        if (FD_ISSET(fd, &read_fds)) {
            char buf[1000] = "";

            if (read(fd, buf, sizeof(buf)) < 0) {
                perror("error reading fifo");
            }

            printf("read: \"%s\"\n", buf);

            if (strcmp(buf, "quit\n") == 0) {
                break;
            }
        }

        // If write
        if (FD_ISSET(fd, &write_fds)) {
            char *buf = "foo";

            if (write(fd, buf, sizeof(buf)) < 0) {
                perror("error writing fifo");
            }

            printf("write: \"%s\"\n", buf);
        }
    }

    // Close fifo
    if (close(fd) < 0) { // Failed to close
        perror("failed to close export fifo");
    }

    return 0;
}

通过下载代码from here (GitHub Gist) 运行示例。然后运行:

gcc -o fifo fifo.c
./fifo

输出将显示读写之间的循环:

write: "foo"
read: ""
write: "foo"
read: ""
write: "foo"
...

【问题讨论】:

  • 在你的代码中没有地方 quit,所以我希望它无限循环(例如你只写foo)。你想在什么条件下写quit(例如在第N条写消息之后)?
  • 这段代码应该做什么?
  • 您通常不会在单个进程中打开 FIFO 进行读写。一个给定的进程要么读取 FIFO,要么写入 FIFO。我认为你的设计有问题——你有一个 XY Problem 某种形状或形式,我们不知道 X 是什么,所以 Y 看起来有点奇怪,如果不是离谱的话。
  • 正如乔纳森所指出的,我们需要第二个过程。因此,除非您可以发布该过程的代码,否则我们无法帮助调试。此外,fifos 是单向的(写入器和读取器),不像 像套接字。因此,要使用 fifos 执行此操作,您需要 两个 。 (例如)给定进程AB,我们需要两个管道/fifo:pipeABpipeBA。进程A 写入pipeABBpipeAB 读取。进程B 写入pipeBAApipeBA 读取
  • 如果你想使用套接字,你可以使用PF_UNIX(又名AF_UNIX)套接字。请参阅 man 7 unixman 2 socketpair。或者,您可以做一个完整的AF_INET 套接字,将主机设置为localhost,并使用一些固定的端口号。作为[为您]的练习,请考虑以多种方式进行。也就是说,argv 选项,例如用于双管道的-Tp,用于AF_UNIX-Tu,以及用于AF_INET-Ts,等等。只有初始化会有所不同。否则协议将几乎相同。

标签: c select file-descriptor


【解决方案1】:

注意:这是由我的顶级 cmets 开头的。

我们需要两个进程(例如一个服务器和一个客户端)。

fifos 是单向的(写入器和读取器),不像 像套接字。

因此,要使用 fifos 执行此操作,您需要其中两个。 (例如)给定进程 A 和 B,我们需要两个管道/fifo:pipeABpipeBA

进程 A 写入 pipeAB,B 读取 pipeAB

进程 B 写入 pipeBA 而 A 从 pipeBA 读取

如果你想使用套接字,你可以使用PF_UNIX(又名AF_UNIX)套接字。请参阅man 7 unixman 2 socketpair

或者,您可以做一个完整的 AF_INET 套接字,将主机设置为 localhost,并使用一些固定的端口号。

作为[为您]的练习,请考虑以多种方式进行。也就是说,argv 选项,例如用于双管道的-Tp,用于 AF_UNIX 的-Tu,以及用于 AF_INET 的-Ts,等等。只有初始化会有所不同。否则协议几乎相同。

对于AF_UNIXsockets,如果客户端和服务器是不同的程序,在文件系统中创建一个socket类型的文件可能会更容易一些。这可以通过使用“文件名”填写struct sockaddr_un,然后在socket 调用之后使用bind 来完成。请参阅:https://www.ibm.com/support/knowledgecenter/en/SSB23S_1.1.0.13/gtpc1/unixsock.html 示例

【讨论】:

    猜你喜欢
    • 2014-04-09
    • 2021-09-28
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    • 2016-05-01
    • 2020-11-13
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多