【发布时间】: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 执行此操作,您需要 两个 。 (例如)给定进程
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,等等。只有初始化会有所不同。否则协议将几乎相同。
标签: c select file-descriptor