Linux管道写入(write())操作超时

3

我该如何在Linux管道上为write()设置超时?

示例代码:

int fd_pipe = open("/run/some/pipe", O_RDWR);

// here i need to set timeout for 3 seconds somehow, if can't write, code will continue...
write(fd_pipe, something, strlen(something));

// continue executing..

谢谢


“write”调用太低级了,直接实现这一点是不可能的;有几种方法可以解决这个问题(例如,在非阻塞模式下打开文件或设置警报信号)。或者,您可以尝试更高级别的调用,这可能更好。 - Daniel H
打开一个FIFO O_READO_WRITE,但永远不要同时打开两个,这取决于你使用它的角色。如果你向FIFO写入数据,然后从中读取,你将得到之前写入的数据。FIFO没有套接字语义。如果读取器关闭了写入FIFO,你不会收到通知,因为你是该FIFO的另一个读取器。如果你想要双向通信,请打开一个Unix套接字或使用两个FIFO。 - Luis Colorado
1个回答

3

就像对于网络套接字一样,您可以在管道上使用select()来查看是否会阻塞write()

首先,初始化fdset和超时时间:

fd_set fds;
FD_ZERO(&fds);
FD_SET(fd_pipe, &fds);
struct timeval tv = { 3, 0 }; // 3 secs, 0 usecs

以下调用等待最多3秒钟(如tv中指定)使管道可写。
int st = select(fd_pipe+1, NULL, &fds, NULL, &tv);
if (st < 0) {
    // select threw an error
    perror("select");
else if (FD_ISSET(fd_pipe, &fds)) {
    int bytes = write(fd_pipe, something, strlen(something));
} else {
    // Writing not possible in 3 seconds, wait
}

当然,您需要检查write()调用的返回值(无论哪种情况),因为可能会发生写入管道的字节数比请求的字节数的情况。


1
不,这并不真正起作用,即使对于套接字也是如此,管道可写的事实最多意味着您可以在不阻塞的情况下将一个字节写入其中。 对于甚至套接字,众所周知例如Linux可以报告套接字可读,但当您尝试从中读取时,内核会注意到TCP数据包存在校验和错误,并且需要请求另一端进行重传... - Antti Haapala -- Слава Україні
1
你唯一能做的就是选择,在 O_NONBLOCK 模式下尝试写入,如果不起作用则重试选择。 - Antti Haapala -- Слава Україні
简而言之,select() 是可以的。只是文件描述符需要设置 O_NONBLOCK 才能按预期工作(可以在 open() 时或通过 fcntl 设置)。 - P.P
@AnttiHaapala 我不明白你的评论与此有何关系;是的,也许只能写入一个字节,但它不会阻塞。write() 的返回值将小于传递的缓冲区大小,这必须处理。我在答案中明确解决了这个问题。其余部分讨论读取,与此完全无关。 - Ctx
1
@AnttiHaapala 顺便说一下,TCP 不知道“请求重传”的机制,数据包只会被丢弃。另外,我认为 select manpage 中关于 TCP 校验和的段落是不准确的,这种情况永远不会发生。 - Ctx
@AnttiHaapala,TCP 重传发生在发送方,当 ACK 超时时。高级套接字代码只是表现得好像没有数据正在传输,因此它必须阻塞等待数据。 - Luis Colorado

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接