假设我在Linux系统上创建了一个命名管道:
$ mkfifo my_pipe
接下来要做的是编写一个小的监视程序,该程序尝试从read()
移至my_pipe
,但过一会儿就会超时。在下面的伪代码中,我使用了虚构函数wait_for_avail(fd, timeout_ms)
:
int fd = open("my_pipe", O_RDONLY);
while (1) {
//Fictional wait_for_avail(fd, timeout_ms). Is there a real function
//that has this behaviour?
int rc = wait_for_avail(fd, 500);
if (rc == 1) {
char buf[64];
read(fd, buf, 64);
//do something with buf
} else {
fprintf(stderr, "Timed out while reading from my_pipe\n");
//do something else in the program
}
}
我以为带有poll
标志的POLLIN
可能有用,但没有用。从我的简单试验中,我发现它只是等到另一个进程已经[[open]]命名管道进行写入(但没有可用的数据,即read()
不会阻塞)。顺便说一下,由于某种原因,poll
会忽略您的超时,并且似乎永远阻塞直到另一个进程打开管道为止。[我唯一想到的另一种解决方案是用open()
来O_NONBLOCK
文件,并在我不断尝试以0字节为计数的read()
时手动观察时间的流逝。
那里有更好的解决方案吗?
编辑:我在这里打开打开命名管道的过程受阻。但是,如果使用O_NONBLOCK
标志,则文件将立即打开。在这一点上,poll()
可用于等待(打开超时)等待打开管道的另一端以进行写入。
但是,这仍然具有为read()
函数实现超时的行为。调用read()
后,它似乎仍然会阻塞(即使使用O_NONBLOCK
打开了管道)
假设我在Linux系统上创建了一个命名管道:$ mkfifo my_pipe接下来要做的是编写一个小的监视程序,该程序尝试从my_pipe读取(),但过一会儿就会超时。在...
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
while (1) {
int fd = open("my_pipe", O_RDONLY | O_NONBLOCK);
if (fd < 0) {
perror("open");
return EXIT_FAILURE;
}
struct pollfd waiter = {.fd = fd, .events = POLLIN};
while (1) {
// 10 second timeout
switch (poll(&waiter, 1, 10 * 1000)) {
case 0:
puts("The fifo timed out.");
break;
case 1:
if (waiter.revents & POLLIN) {
char buffer[BUFSIZ];
ssize_t len = read(fd, buffer, sizeof buffer - 1);
if (len < 0) {
perror("read");
return EXIT_FAILURE;
}
buffer[len] = '\0';
printf("Read: %s\n", buffer);
} else if (waiter.revents & POLLERR) {
puts("Got a POLLERR");
return EXIT_FAILURE;
} else if (waiter.revents & POLLHUP) {
// Writer closed its end
goto closed;
}
break;
default:
perror("poll");
return EXIT_FAILURE;
}
}
closed:
if (close(fd) < 0) {
perror("close");
return EXIT_FAILURE;
}
}
}
read()
调用之前设置计时器。然后,我们可以简单地检查read()
的返回值以查看它是否由于超时而中断,是否发生了一般错误,是否达到了EOF或是否成功读取了数据。只有一个障碍:您不能以非阻止模式打开文件;这会导致open()
阻塞,直到另一个进程打开要写入的管道为止。但是,在我的应用程序中,这实际上是一个理想的功能。您也可以设置SIGALRM以对open()
强制执行超时,或者在另一个线程中执行超时。
实际上,此技术应与任何其他系统调用一起使用,因此我可能会放一些帮助程序库,以使此模式更易于使用。编辑
另外一件事:注册信号处理程序时不要使用
SA_RESTART
标志非常重要。否则,即使系统调用被信号中断,Linux也会在处理信号后重试。