选择命名管道(FIFO)会导致无限循环

问题描述 投票:0回答:1

我有一个循环。在此循环中,我尝试使用

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;
}

通过从此处下载代码运行示例 (GitHub Gist)。然后运行:

gcc -o fifo fifo.c
./fifo

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

write: "foo"
read: ""
write: "foo"
read: ""
write: "foo"
...
c file-descriptor posix-select
1个回答
1
投票

注意: 这是我的热门评论的序言。

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

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

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

pipeAB
pipeBA

进程 A 写入

pipeAB
,B 从
pipeAB
读取。

进程 B 写入

pipeBA
,A 则从
pipeBA

读取

如果你想使用套接字,你可以做一个

PF_UNIX
(又名
AF_UNIX
)套接字。请参阅
man 7 unix
man 2 socketpair

或者,您可以创建一个完整的 AF_INET 套接字,并将主机设置为具有固定端口号的 localhost。

作为[对你来说]的一项练习,请考虑以多种方式进行。也就是说,

argv
选项,例如双管道的
-Tp
,AF_UNIX的
-Tu
,AF_INET的
-Ts
等。只是初始化会有所不同。否则协议几乎是相同的。

对于

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

© www.soinside.com 2019 - 2024. All rights reserved.