文件监控直接通过了linux的inotify接口实现。这里没有考虑移植性,也就没像tailf那样,通过宏来判断是否支持inotify,如果不支持,降级使用循环轮寻的方式读取。
inotify的使用还是比较方便的基本上就是:inotify_init,inotify_add_watch,然后配合read系统调用,获取文件修改信息。因此实现也非常方便。
首先是在构造函数里面初始化inotify:
[cce lang=”cpp”]
inotifyfd = inotify_init();
[/cce]
然后提供一个watch接口,通过传入前文描述的tfile对象和内容读取的回调函数,添加对应文件的监控和回调。
void filewatcher::watch ( boost::shared_ptr< tfile > tfile, std::list< filewatcher::readcallback > callbacklist )
{
if(!tfile->haserror() && !callbacklist.empty()) {
int wd = inotify_add_watch(inotifyfd, tfile->name().c_str(), in_modify);
if(wd > 0) {
tfilemap.insert(std::make_pair<int, boost::shared_ptr<tfile> >(wd, tfile));
callbackmap.insert(std::make_pair<int, std::list<readcallback> >(wd, callbacklist));
//init read
std::string initcontent = tfile->read();
boost_foreach(readcallback &callback, callbacklist) {
callback(initcontent);
}
这里通过tfile的文件名,向内核注册添加该文件的modify事件,并且在注册成功之后,进行初始读取(这里有个小问题,由于后面websocket端没有做缓存,所以由于初始读取的时候还没有任何websocket客户端连接,所以通过web无法读取初始内容,也就是文件最后10行)。同时,这个类维护两个hashmap,分别是监听描述符wd->tfile和wd->callbacklist。
监听完成后,就是启动监听,也就是通过读取fd,感知被监听文件的变更,由于这里只监听了文件修改,那么读取到这个事件之后,就可以对该文件进行增量读取(前文已经描述了读取方法)。
char * buffer = new char[inotifybuffersize];
while(!_interrupted) {
if(read(inotifyfd, buffer, inotifybuffersize) < 0) {
if(errno == eintr) {
// interrupt
delete buffer;
return;
struct inotify_event *event = ( struct inotify_event * ) buffer;
int wd = event->wd;
boost_auto(tfileiter, tfilemap.find(wd));
if(tfileiter != tfilemap.end()) {
boost::shared_ptr<tfile> tfile = tfileiter->second;
std::string content = tfile->read();
boost_auto(iter, callbackmap.find(wd));
if(iter != callbackmap.end()) {
std::list<readcallback> callbacks = iter->second;
boost_foreach(readcallback &callback, callbacks) {
callback(content);
这里参照inotify的文档,首先读取缓冲区大小设置为
static const int inotifybuffersize = sizeof(struct inotify_event) + name_max + 1;
也就是inotify_event结构的长度,和名字最大值。由于inotify_event是变长字段(包含一个可选的文件名字段),所以这里采用了系统限制的文件名最大值name_max,这个宏在climits中定义,在linux中大小为255字节。
然后通过系统调用read,读取文件描述符inotifyfd,这里如果没有新的事件产生,read会进入阻塞状态,节省系统资源。如果有返回,则处理返回的inotify_event对象(注意在监听modify事件的时候,是没有文件名的)。通过结构中的wd,从之前保存的hashmap中获取对应的tfile对象进行增量读取,然后再读取wd对应的回调函数,将读取内容返回。
这里有个小问题需要处理,就是如何中断读取。之前为了在gtest中能够通过单元测试的方式进行测试,通过查看手册可以知道,如果read调用被系统信号中断,会标记错误码为eintr。所以,当读取失败的时候,可以通过对errno检查,判断是否是信号中断。
由于程序会一直运行,知道通过信号终止,所以析构变的不是很重要了。这里析构函数里面通过调用inotify_rm_watch将之前保存的wd全部去掉,然后通过close调用交inotify的文件描述符关闭即可:
filewatcher::~filewatcher()
if(inotifyfd > 0) {
boost::unordered::unordered_map<int, boost::shared_ptr<tfile> >::iterator iter;
for(iter = tfilemap.begin(); iter != tfilemap.end(); ++iter) {
inotify_rm_watch(inotifyfd, iter->first);
close(inotifyfd);
转载自:https://coolex.info/blog/406.html