如何在不使用read()的情况下清空文件描述符?

15
注意:这 不是 有关如何清空 write() 的问题。这是它的 另一端,可以这么说。
如果一个文件描述符中有数据等待读取,那么是否有可能在不必执行 read() 的情况下将其清空?您可能对这些数据不感兴趣,因此读取它们会浪费空间和循环,而这些资源可能有更好的用途。
如果在 POSIX 中无法实现,那么任何操作系统是否有任何非便携式的方法可以做到这一点? 更新: 请注意,我说的是 文件描述符,而 不是 流。
8个回答

7

如果你正在处理一个tty,可以查看tcflush()函数:

#include <termios.h>
int tcflush(int fildes, int queue_selector);

成功完成后,tcflush() 会清除在终端相关的打开文件描述符 fildes 对应的对象中未被传输或未被读取的数据,具体根据队列选择器的值而定。请参考http://opengroup.org/onlinepubs/007908775/xsh/tcflush.html

谢谢,这正是我想要的! - Etheryte

4

2
如果您知道要跳过的字节数,可以在POSIX系统上使用lseek(fd, n, SEEK_CUR);。对于FILE *对象也有fseek()。在POSIX中,我认为可以安全地查找超出文件末尾的位置,因为如果稍后写入更多数据,使数据超过使用lseek()设置的位置,现在您将能够读取更多数据。

2

Linux 2.6.17或更高版本,带有GNU C库版本2.5或更高版本,包含splice()系统调用,可用于将数据从一个文件描述符发送到另一个文件描述符,而无需将其复制到用户空间。这可以通过简单地打开/dev/null并将数据从源文件描述符splice/dev/null文件描述符来丢弃数据。


1

从语言角度来看,fclean显然不是标准的。 - anon
正确。它是GNU libc的一部分。我非常确定几乎每个操作系统都可以使用它。我更新了GNU手册的链接,而不是镜像。 - jdmichal
目前的答案似乎是“lseek”。由于您是第一个提出建议的人,所以我会接受您的答案。我希望有一些可以在管道、FIFO等上工作的东西,这就是为什么我等待的原因,但我想这样的东西不存在。 - Teddy
4
lseek是实际文件的最佳解决方案,但某些文件描述符不可寻址(例如管道和FIFO),因此执行lseek会失败。如果您想要适用于所有类型文件描述符的最通用解决方案,确实需要将其读入临时缓冲区并将其丢弃。为了获得最佳性能,请使用一个相当大的缓冲区,但不要太大以至于超出L1或L2高速缓存的容量。 - Adam Rosenfield
啊,我明白了。我同意亚当的评论,如果lseek不起作用,那么唯一的选择就是读取并丢弃数据。 - jdmichal
这个答案可以改进:我最初以为会涉及到上述的 fclean,但第一个链接却谈到了 fflush()。我在其他地方也找不到有关 fclean 的任何信息。第二段甚至没有提到相关的函数(结果是 lseek())。因此,整个答案有些令人困惑,并且完全依赖于链接内容。 - domsson

1

read()和flush()都不是标准C或C++的一部分,但肯定没有任何标准函数支持输入流的刷新。我猜这反映了底层操作系统中缺少某些功能。避免完全读取某些内容的正常方法是使用某种类型的seek()函数跳过它。


0
根据this,在POSIX系统上执行fflush(stream);会发生以下情况。
对于一个打开进行读取的流,如果文件尚未到达EOF,并且该文件能够寻址,则应调整底层打开文件描述符的文件偏移量,以便下一次操作处理从流中刷新的最后一个字节之后的字节。

这个涵盖了以下两个方面:(a) 在单个文件上交错缓冲IO时同步读和写位置,以及(b) 撤消已经超过请求的读取缓冲区的效果。不是OP所要求的内容。 - ephemient
那是针对流而不是文件描述符的。 - Teddy

0

BSD引入了fpurge(),Solaris和glibc引入了__fpurge()
来自man page

#include <stdio.h>
#include <stdio_ext.h>
void  __fpurge(FILE *stream);

函数__fpurge()清除给定流的缓冲区。对于输出流,这会丢弃任何未写入的输出。对于输入流,这会丢弃从底层对象读取但尚未通过getc(3)获取的任何输入;这包括通过ungetc(3)推回的任何文本。
函数__fpurge()执行完全相同的操作,但不返回值。
请注意:
这些函数是非标准的且不可移植的。函数fpurge()在4.4BSD中引入,在Linux下不可用。函数__fpurge()在Solaris中引入,并在glibc 2.1.95及更高版本中存在。
如果您正在使用文件描述符,您可能需要先从描述符获取FILE*
int fd;
/* ... */
FILE* fp = fdopen(fd, "r");
/* ... */
__fpurge(fp);

我认为这不会有帮助。它将删除程序自己的stdio缓冲区中的任何内容(如果您刚刚fdopen,则为空),但它不会影响可以从操作系统读取的数据。 - Nate Eldredge

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