檔案監控直接通過了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