简体   繁体   English

如何读取附加到(又名像tail -f)的文件的[nonblocking]文件描述符?

[英]How to read a [nonblocking] filedescriptor of a file that is appended to (aka, like tail -f)?

Actually, I am using libev; 实际上,我正在使用libev。 but under the hood this is using epoll (I'm only on linux). 但这实际上是使用epoll(我仅在linux上)。 When I add a watcher to read a file and all data has been read then I do get a call back that there is data to read, but read(2) returns 0 (EOF). 当我添加观察者以读取文件并且所有数据都已被读取时,我的确收到了要读取的数据的调用,但read(2)返回0(EOF)。 At that point I have to stop the watcher or else it will continue to tell me that there is something to read. 在这一点上,我必须停止观察程序,否则它将继续告诉我有一些需要阅读的内容。 However, if I stop the watcher and then some other process appends data to that file then I'll never see it. 但是,如果我停止监视程序,然后其他一些过程将数据追加到该文件,那么我将永远看不到它。

What is the correct way to get notified that there is additional/appended data in a file that can be read when before I already read till the end? 在我已经读到结束之前可以读取文件中是否有其他附加信息的通知的正确方法是什么?

I'd prefer the answer in terms of libev, but lower level will do too (I can then probably translate that to how to do that with libev). 我更喜欢libev的答案,但是较低级别的答案也可以(我可以将其转换为使用libev的方法)。

It is very common, for some reason, for people to think that making an fd nonblocking, or calling poll/select/.. has different behaviour for files compared to other types of file descriptions, but nonblocking behaviour and I/O readyness behaviour is essentially the same for all of types of file descriptions: the kernel will immediately return from read/write etc. if the outcome is known, and will signal I/O readyness when this is the case. 由于某些原因,人们通常认为与其他类型的文件描述相比,使fd为非阻塞或调用poll / select / ..对文件具有不同的行为,但是非阻塞行为和I / O准备行为是对于所有类型的文件描述基本相同:如果知道结果,内核将立即从读/写等返回,并且在这种情况下将发出I / O准备就绪的信号。 When a socket has an EOF condition, select will signal that the socket is ready to read, and you will get 0 (for EOF). 当套接字处于EOF状态时,select将发出信号,表明该套接字已准备好读取,并且您将获得0(对于EOF)。 The same happens for files - if you are at the end of a file, the kernel will return immediately from read and return 0 to signal EOF. 对于文件也是如此-如果您位于文件末尾,内核将立即从读取返回,并返回0表示信号EOF。

The important difference is that files can change contents at random places, and can be extended. 重要的区别是文件可以在任意位置更改内容,并且可以扩展。 Pipes and sockets are not random access and cannot be appended to once closed. 管道和插座不是随机访问的,一旦关闭便无法追加。 Thus, while the behaviour is consistent, it is often not what is wanted, namely waiting for a file to change in some way. 因此,虽然行为是一致的,但通常不是所希望的,即等待文件以某种方式更改。

The conflict in many people's minds is simply that they want to be told "when there is new data", but if you think about it a bit, you will realise that simply waking you up would not be an adequate interface for this, as you have no way of knowing why you woke up, and what changed. 许多人心中的冲突只是想告诉他们“何时有新数据”,但是如果您再三思量,您将意识到,仅唤醒您是不足够的接口,因为您无法知道您为何醒来以及发生了什么变化。

POSIX doesn't have an interface to do that, other than regularly polling the fd or file (and in case of random changes, regularly reading the whole file!). POSIX除了定期轮询fd或文件(并且在随机更改的情况下,定期读取整个文件!)之外,没有接口可以执行此操作。 Some operating systems have an interface to do something similar to that (kqueue on BSDs, inotify on GNU/Linux) , but they are usually not a perfect match, either (for example, inotify cannot watch an fd for changes, it will watch a path for changes). 某些操作系统具有执行类似操作的界面(在BSD上为kqueue,在GNU / Linux上为inotify),但是它们通常也不是完美的匹配(例如,inotify无法监视fd的更改,它将监视一个fd 。更改路径 )。

The closest you can get with libev is to use an ev_stat watcher. 使用libev可以获得的最接近的结果是使用ev_stat监视程序。 It behaves as if you would stat() a path regularly, and invoke the watcher callback whenever the stat data changes. 它的行为就像您将定期stat()路径,并在stat数据更改时调用观察者回调。 Portably, it does just that: it regularly calls stat , but on some operating systems (currently only inotify on GNU/Linux, as kqueue doesn't have correct semantics for this) it can use other mechanisms to speed this up in some cases, although it will fall back to regular stat polling everywhere, for example for when the file is on a network file system, where inotify can't see remote changes. 可移植的是,它只是这样做:它定期调用stat ,但是在某些操作系统上(目前仅在GNU / Linux上进行inotify,因为kqueue对此不具有正确的语义),它可以在某些情况下使用其他机制来加快速度,尽管它将回落到各地的常规stat轮询,例如,当文件位于网络文件系统上时,inotify无法看到远程更改。

To answer your question: If you have a path, you can use an ev_stat watcher to watch for stat data changes, such as size/mtime etc. changes. 要回答您的问题:如果有路径,则可以使用ev_stat观察程序来监视统计数据更改,例如大小/ mtime等更改。 Doing this right can be a bit tricky (see the libev documentation, especially the part about stat time resolution: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev_stat_code_did_the_file_attri ), and you have to keep in mind that this watches a path , not a file descriptor , so you might want to compare the device/inode of your file descriptor and the watched path regularly to see if you still have the correct file open. 正确执行此操作可能会有些棘手(请参阅libev文档,尤其是有关统计时间解析的部分: http : //pod.tst.eu/http : //cvs.schmorp.de/libev/ev.pod#code_ev_stat_code_did_the_file_attri ) ,并且必须记住,它监视的是路径 ,而不是文件描述符 ,因此您可能需要定期比较文件描述符的设备/索引节点和监视的路径,以查看是否仍打开了正确的文件。

This still doesn't tell you what part of the file has changed. 这仍然不能告诉您文件的哪一部分已更改。

Alternatively, since you apparently only want to read appended data, you could opt to just read() the file regularly (in an ev_timer callback) and do away with all the complexity and hassles of an ev_stat watcher setup (while not forgetting to also compare the path stat data with your fd stat data to see if you still hasve the right file open, depending on whether the file your are reading might get renamed or replaced. Sometimes programs also truncate files, something you can also detect by seeing the size decrease between stat calls). 另外,由于您显然只想读取附加的数据,因此您可以选择仅定期read()文件(在ev_timer回调中),并消除ev_stat观察程序设置的所有复杂性和麻烦(同时不要忘记进行比较)将路径统计数据与fd统计数据一起使用,以查看是否仍在打开正确的文件,具体取决于正在读取的文件是否被重命名或替换。有时程序还会截断文件,通过减小大小也可以检测到统计调用之间)。

This is essentially what older tail -f implementations do, while newer ones might, for example, take hints (only) from inotify, just like ev_stat watchers do. 从本质ev_stat ,这是较旧的tail -f实现的工作,而较新的tail -f实现可能(例如)像ev_stat观察程序那样从inotify获取提示(仅)。

None of that is easy, and details depend on your knowledge of how exactly the file changes, but it's the best you can do. 这些都不是一件容易的事,细节取决于您对文件更改的确切程度的了解,但这是您能做的最好的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM