出于某种原因,人们通常认为使 fd 成为非阻塞或调用 poll/select/.. 与其他类型的文件描述相比对文件具有不同的行为,但非阻塞行为和 I/O 就绪行为是对于所有类型的文件描述基本相同:如果结果已知,内核将立即从读/写等返回,并在这种情况下发出 I/O 就绪信号。当套接字具有 EOF 条件时,select 将发出套接字已准备好读取的信号,您将得到 0(表示 EOF)。文件也会发生同样的情况 - 如果您在文件末尾,内核将立即从读取返回并返回 0 以发出 EOF 信号。
重要的区别是文件可以在随机位置更改内容,并且可以扩展。管道和套接字不是随机访问,一旦关闭就不能附加。因此,虽然行为是一致的,但通常不是想要的,即等待文件以某种方式更改。
很多人心里的矛盾只是想被告知“什么时候有新数据”,但是稍微想一想,你就会发现,仅仅唤醒你并不足以解决这个问题,因为你无法知道你为什么醒来,以及发生了什么变化。
POSIX 没有执行此操作的接口,除了定期轮询 fd 或文件(并且在随机更改的情况下,定期读取整个文件!)。一些操作系统有一个接口来做类似的事情(BSD 上的 kqueue,GNU/Linux 上的 inotify),但它们通常也不是完美的匹配(例如,inotify 不能监视fd的变化,它会监视改变路径)。
与 libev 最接近的是使用ev_stat
观察者。它的行为就像您stat()
定期使用路径一样,并在统计数据更改时调用观察者回调。可移植的,它就是这样做的:它定期调用stat
,但在某些操作系统上(目前仅在 GNU/Linux 上为 inotify,因为 kqueue 对此没有正确的语义)它可以在某些情况下使用其他机制来加速它,尽管它将回退到stat
各处的常规轮询,例如当文件位于网络文件系统上时,inotify 无法看到远程更改。
回答你的问题:如果你有一个路径,你可以使用一个ev_stat
观察者来观察统计数据的变化,比如大小/时间等变化。正确执行此操作可能有点棘手(请参阅 libev 文档,尤其是有关统计时间分辨率的部分:http://pod.tst.eu/http: //cvs.schmorp.de/libev/ev.pod#code_ev_stat_code_did_the_file_attri) ,并且您必须记住,这会监视路径,而不是文件描述符,因此您可能需要定期比较文件描述符的设备/inode 和监视的路径,以查看是否仍然打开了正确的文件。
这仍然不能告诉您文件的哪一部分已更改。
或者,由于您显然只想读取附加数据,您可以选择read()
定期(在ev_timer
回调中)只读取文件并消除ev_stat
观察者设置的所有复杂性和麻烦(同时不要忘记将路径统计数据与你的 fd stat 数据,看看你是否仍然打开了正确的文件,这取决于你正在阅读的文件是否会被重命名或替换。有时程序也会截断文件,你也可以通过查看 stat 调用之间的大小减小来检测)。
这基本上是旧tail -f
实现所做的,而较新的实现可能,例如,(仅)从 inotify 中获取提示,就像ev_stat
观察者一样。
这一切都不是一件容易的事,细节取决于您对文件更改方式的了解,但这是您能做的最好的事情。