天天看点

webtail——文件监控

文件监控直接通过了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

继续阅读