C++利用inotify+epoll實現(xiàn)異步文件監(jiān)控的方法
需求
動態(tài)監(jiān)測linux系統(tǒng)某一個目錄下文件的變化。具體使用場景如linux下應(yīng)用程序運行時產(chǎn)生日志文件,尤其在程序出現(xiàn)某種異常時,日志文件記錄著錯誤出現(xiàn)的原因、時間及代碼位置等信息,此時日志文件在增長,但是采用輪詢的方式定時查看日志文件尤為消耗性能。基于此問題,采用**“epoll+inotify異步文件監(jiān)控”**的方式可以實現(xiàn)日志的動態(tài)刷新。
inotify
特點
inotify是一種異步文件監(jiān)控機(jī)制,主要包括以下特點:
- 可監(jiān)控目錄或文件訪問、讀寫、權(quán)限、刪除、移動等文件系統(tǒng)事件;
- 監(jiān)控目錄時,可監(jiān)控目錄下的子目錄或文件,但是不能遞歸監(jiān)控子目錄的目錄或文件;
- 事件發(fā)生時,由內(nèi)核空間主動回調(diào)至用戶空間,具體為內(nèi)核將事件加入inotify事件隊列,用戶可通過read讀?。?/li>
流程
1)inotify初始化,調(diào)用inotify_init(),返回inotifyFd句柄;
2)創(chuàng)建epoll句柄,調(diào)用epoll_create();
3)注冊epoll事件,調(diào)用epoll_ctl,將inotifyFd添加至epoll_event結(jié)構(gòu)體;
4)將目錄或文件添加為watch對應(yīng),調(diào)用inotify_add_watch();
5)啟動線程等待epoll事件,調(diào)用epoll_wait();
6)有epoll事件發(fā)生時,調(diào)用read函數(shù)inotify事件;
7)解析inotify_event進(jìn)行事件分析;
接口
epoll
- 創(chuàng)建epoll句柄
#include <sys/epoll.h> int epoll_create(int size); ##參數(shù) - size:監(jiān)聽的事件數(shù)目;
- 注冊epoll事件
#include <sys/epoll.h> int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); ## 參數(shù) - epfd:epoll_create()返回值; - op:執(zhí)行的操作。包括EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL; - fd:文件描述符; - event:結(jié)構(gòu)體epoll_event的指針。 #### 結(jié)構(gòu)體epoll_event typedef union epoll_data { void *ptr; /* Pointer to user-defind data */ int fd; /* File descriptor */ uint32_t u32; /* 32-bit integer */ uint64_t u64; /* 64-bit integer */ } epoll_data_t; struct epoll_event { uint32_t events; /* epoll events(bit mask) */ epoll_data_t data; /* User data */ };
- 事件等待
#include <sys/epoll.h> int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); ## 參數(shù): - epfd:epoll_create()返回值; - events:evlist所指向的結(jié)構(gòu)體數(shù)組中返回的是有關(guān)就緒態(tài)文件描述符的信息,數(shù)組evlist的空間由調(diào)用者負(fù)責(zé)申請; - maxevents:指定所evlist數(shù)組里包含的元素個數(shù); - timeout:確定epoll_wait()的阻塞行為。<0:一直阻塞,0:執(zhí)行一次非阻塞式地檢查,>0:調(diào)用將阻塞至多timeout毫秒;
inotify
- 初始化
#include <sys/inotify.h> int inotify_init(void); ## 返回值 - 返回文件描述符
- 添加監(jiān)控對象
#include <sys/inotify.h> int inotify_add_watch(int fd, const char *pathname, uint32_t mask); ## 參數(shù) - fd: inotify_init的返回值; - pathname: 目錄或文件路徑; - mask: 監(jiān)控的事件類型; ## 返回值 - 針對pathname的watch描述符
mask事件列表:
事件 | 描述 |
---|---|
IN_ACCESS | 文件被訪問 |
IN_ATTRIB | 元數(shù)據(jù)被改變,例如權(quán)限、時間戳、擴(kuò)展屬性、鏈接數(shù)、UID、GID等 |
IN_CLOSE_WRITE | 打開用于寫的文件被關(guān)閉 |
IN_CREATE | 在監(jiān)控的目錄中創(chuàng)建了文件或目錄 |
IN_DELETE | 在監(jiān)控的目錄中刪除了文件或目錄 |
IN_DELETE_SELF | 監(jiān)控的文件或目錄本身被刪除 |
IN_CLOSE_NOWRITE | 不是打開用于寫的文件被關(guān)閉 |
IN_MODIFY | 文件被修改 |
IN_MOVE_SELF | 監(jiān)控的文件或目錄本身被移動 |
IN_MOVED_FROM | 從監(jiān)控的目錄中移出文件 |
IN_MOVED_TO | 向監(jiān)控的目錄中移入文件 |
IN_OPEN | 文件或目錄被打開 |
IN_ALL_EVENTS | 包含了上面提到的所有事件 |
- 移除監(jiān)控對象
#include <sys/inotify.h> int inotify_rm_watch(int fd, int wd); ## 參數(shù)值 - fd: inotify_init的返回值; - wd: inotify_add_watch的返回值;
示例
- 代碼示例
#include <iostream> #include <thread> #include <sys/inotify.h> #include <sys/epoll.h> #include <unistd.h> using namespace std; #define INOTIFY_FDS 200 #define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event)) #define INOTIFY_BUF_LEN (1024*(INOTIFY_EVENT_SIZE + 16)) int main() { // inotify初始化 int inotifyId = inotify_init(); if (-1 == inotifyId) { cout << "inotify_init failed" << endl; return -1; } // 創(chuàng)建epoll句柄 int epfd = epoll_create(INOTIFY_FDS); if (-1 == epfd) { cout << "epoll_create failed" << endl; return -1; } // 注冊epoll事件 struct epoll_event ev; ev.data.fd = inotifyId; ev.events = EPOLLIN | EPOLLET; int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, inotifyId, &ev); if (-1 == ret) { cout << "epoll_ctl failed" << endl; return -1; } // 添加監(jiān)聽對象 const char* pathMame = "/home/inotify/dir/"; int watchFd = inotify_add_watch(inotifyId, pathMame, IN_MODIFY | IN_CREATE | IN_DELETE); if (watchFd < 0) { cout << "inotify_add_watch failed" << endl; return -1; } // 啟動線程 std::thread func = std::thread([&]() { // 循環(huán)監(jiān)聽事件 char buf[INOTIFY_BUF_LEN] = { 0, }; struct epoll_event events[20]; while (1) { int nfds = epoll_wait(epfd, events, 20, 1000); for (int i = 0; i < nfds; ++i) { if (events[i].data.fd != inotifyId) { continue; } int length = read(inotifyId, buf, INOTIFY_BUF_LEN); if (length < 0) { // error continue; } int pos = 0; while (pos < length) { struct inotify_event* event = (struct inotify_event*)&buf[pos]; if (event->len) { // 此處(event->wd == watchFd) if (event->mask & IN_CREATE) { if (event->mask & IN_ISDIR) { // dir create cout << "dir create" << endl; } else { // file create cout << "file create" << endl; } } else if (event->mask & IN_DELETE) { if (event->mask & IN_ISDIR) { // dir delete cout << "dir delete" << endl; } else { // file delete cout << "file delete" << endl; } } else if (event->mask & IN_MODIFY) { if (event->mask & IN_ISDIR) { // dir modify cout << "dir modify" << endl; } else { // file modify cout << "file modify" << endl; } } } pos += INOTIFY_EVENT_SIZE + event->len; } } // 1s更新一次 sleep(1); } }); func.join(); while (1) { // 僅作事件循環(huán) sleep(2); } inotify_rm_watch(inotifyId, watchFd); close(epfd); close(inotifyId); return 0; }
- 執(zhí)行結(jié)果
[root@localhost inotify]# ./testInotify
file create
dir create
file create
file modify
file create
file modify
dir delete
file delete
到此這篇關(guān)于C++利用inotify+epoll實現(xiàn)異步文件監(jiān)控的方法的文章就介紹到這了,更多相關(guān)C++ inotify+epoll異步文件監(jiān)控內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++的get()函數(shù)與getline()函數(shù)使用詳解
這篇文章主要介紹了C++的get()函數(shù)與getline()函數(shù)使用詳解,是C++入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-09-09C++實現(xiàn)字符格式相互轉(zhuǎn)換的示例代碼
這篇文章主要為大家詳細(xì)介紹了C++中實現(xiàn)字符格式相互轉(zhuǎn)換的方法,主要有UTF8與string互轉(zhuǎn)、wstring與string互轉(zhuǎn),感興趣的小伙伴可以了解一下2022-11-11Eclipse中C++連接mysql數(shù)據(jù)庫
這篇文章主要為大家詳細(xì)介紹了Eclipse中C++連接mysql數(shù)據(jù)庫 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06C語言 超詳細(xì)介紹與實現(xiàn)線性表中的帶頭雙向循環(huán)鏈表
帶頭雙向循環(huán)鏈表:結(jié)構(gòu)最復(fù)雜,一般用在單獨存儲數(shù)據(jù)。實際中使用的鏈表數(shù)據(jù)結(jié)構(gòu),都是帶頭雙向循環(huán)鏈表。另外這個結(jié)構(gòu)雖然結(jié)構(gòu)復(fù)雜,但是使用代碼實現(xiàn)以后會發(fā)現(xiàn)結(jié)構(gòu)會帶來很多優(yōu)勢,實現(xiàn)反而簡單2022-03-03海量數(shù)據(jù)處理系列之:用C++實現(xiàn)Bitmap算法
本篇文章是對用C++實現(xiàn)Bitmap算法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05VSCode插件開發(fā)全攻略之打包、發(fā)布、升級的詳細(xì)教程
這篇文章主要介紹了VSCode插件開發(fā)全攻略之打包、發(fā)布、升級的教程,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05