如何读取附加到的文件的[nonblocking]文件描述符(也就像tail -f一样)?

Car*_*ood 1 linux epoll file nonblocking libev

实际上,我正在使用libev; 但在幕后这是使用epoll(我只在Linux上).当我添加一个观察者来读取文件并且所有数据都已被读取时,我确实得到一个回调,即有数据要读取,但是read(2)返回0(EOF).那时我必须停止观察者,否则它会继续告诉我有东西要读.但是,如果我停止观察者,然后其他一些进程将数据附加到该文件,那么我将永远不会看到它.

获得通知的正确方法是,在我读完之前可以读取的文件中是否有附加/附加数​​据?

我更喜欢libev方面的答案,但是更低级别也会这样做(我可以将其转化为如何用libev做到这一点).

Rei*_*ica 7

由于某种原因,人们认为制作fd非阻塞或调用poll/select/..与其他类型的文件描述相比具有不同的文件行为是非常常见的,但非阻塞行为和I/O就绪行为是对于所有类型的文件描述基本相同:如果结果已知,内核将立即从读/写等返回,并且在这种情况下将发出I/O准备就绪的信号.当套接字具有EOF条件时,select将发出信号表示套接字已准备好读取,并且您将获得0(对于EOF).文件也是如此 - 如果你在文件的末尾,内核将立即从读取返回并返回0到信号EOF.

重要的区别是文件可以在随机位置更改内容,并且可以扩展.管道和套接字不是随机访问,一旦关闭就无法附加.因此,虽然行为是一致的,但通常不是想要的,即等待文件以某种方式改变.

许多人心中的冲突只是他们希望被告知"当有新数据时",但如果你想一想,你就会意识到简单地唤醒你并不是一个适当的界面,就像你一样无法知道你为什么醒来,改变了什么.

除了定期轮询fd或文件(并且在随机更改的情况下,定期读取整个文件!),POSIX没有这样做的接口.有些操作系统有一个接口可以做类似的事情(BSD上的kqueue,GNU/Linux上的inotify),但它们通常也不是完美的匹配(例如,inotify无法观察fd的变化,它会观看改变的道路).

libev最接近的是使用ev_stat观察者.它的行为就像您stat()定期路径一样,并在统计数据发生变化时调用观察者回调.可以这么说,它只是这样:它经常调用stat,但在某些操作系统上(目前只在GNU/Linux上进行inotify,因为kqueue没有正确的语义),在某些情况下,它可以使用其他机制来加快速度,尽管它将回退到stat每个地方的常规轮询,例如当文件位于网络文件系统上时,其中inotify无法看到远程更改.

要回答您的问题:如果您有路径,则可以使用ev_stat观察程序来监视统计数据更改,例如大小/ mtime等更改.这样做可能有点棘手(参见libev文档,特别是关于统计时间分辨率的部分:http://pod.tst.eu/http : //cvs.schmorp.de/libev/ev.pod#code_ev_stat_code_did_the_file_attri) ,你必须记住,这手表一个路径,而不是一个文件描述符,所以你可能要定期比较您的文件描述符的设备/索引节点和观看路径,看看您是否仍然有正确的文件打开.

这仍然不能告诉您文件的哪个部分已更改.

或者,由于您显然只想读取附加数据,您可以选择仅read()定期(在ev_timer回调中)文件,并消除ev_stat观察者设置的所有复杂性和麻烦(同时不要忘记也将路径统计数据与你的fd stat数据,看看你是否还有正确的文件打开,这取决于你正在阅读的文件是否可能被重命名或替换.有时程序也截断文件,你也可以通过查看stat调用之间的大小减少来检测.

这实际上是旧tail -f实现的功能,而较新的实现可能会从inotify中获取提示(仅限于),就像ev_stat观察者一样.

这一切都不容易,细节取决于您对文件更改方式的了解,但这是您可以做的最好的.