C++利用inotify+epoll实现异步文件监控的方法
作者:却道天凉_好个秋
这篇文章讲给大家详细介绍一下C++利用inotify+epoll实现异步文件监控的方法,inotify是一种异步文件监控机制,文章通过代码示例介绍的非常详细,具有一定的参考价值,需要的朋友可以参考下
需求
动态监测linux系统某一个目录下文件的变化。具体使用场景如linux下应用程序运行时产生日志文件,尤其在程序出现某种异常时,日志文件记录着错误出现的原因、时间及代码位置等信息,此时日志文件在增长,但是采用轮询的方式定时查看日志文件尤为消耗性能。基于此问题,采用**“epoll+inotify异步文件监控”**的方式可以实现日志的动态刷新。
inotify
特点
inotify是一种异步文件监控机制,主要包括以下特点:
- 可监控目录或文件访问、读写、权限、删除、移动等文件系统事件;
- 监控目录时,可监控目录下的子目录或文件,但是不能递归监控子目录的目录或文件;
- 事件发生时,由内核空间主动回调至用户空间,具体为内核将事件加入inotify事件队列,用户可通过read读取;
流程
1)inotify初始化,调用inotify_init(),返回inotifyFd句柄;
2)创建epoll句柄,调用epoll_create();
3)注册epoll事件,调用epoll_ctl,将inotifyFd添加至epoll_event结构体;
4)将目录或文件添加为watch对应,调用inotify_add_watch();
5)启动线程等待epoll事件,调用epoll_wait();
6)有epoll事件发生时,调用read函数inotify事件;
7)解析inotify_event进行事件分析;
接口
epoll
- 创建epoll句柄
#include <sys/epoll.h> int epoll_create(int size); ##参数 - size:监听的事件数目;
- 注册epoll事件
#include <sys/epoll.h> int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); ## 参数 - epfd:epoll_create()返回值; - op:执行的操作。包括EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL; - fd:文件描述符; - event:结构体epoll_event的指针。 #### 结构体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); ## 参数: - epfd:epoll_create()返回值; - events:evlist所指向的结构体数组中返回的是有关就绪态文件描述符的信息,数组evlist的空间由调用者负责申请; - maxevents:指定所evlist数组里包含的元素个数; - timeout:确定epoll_wait()的阻塞行为。<0:一直阻塞,0:执行一次非阻塞式地检查,>0:调用将阻塞至多timeout毫秒;
inotify
- 初始化
#include <sys/inotify.h> int inotify_init(void); ## 返回值 - 返回文件描述符
- 添加监控对象
#include <sys/inotify.h> int inotify_add_watch(int fd, const char *pathname, uint32_t mask); ## 参数 - fd: inotify_init的返回值; - pathname: 目录或文件路径; - mask: 监控的事件类型; ## 返回值 - 针对pathname的watch描述符
mask事件列表:
事件 | 描述 |
---|---|
IN_ACCESS | 文件被访问 |
IN_ATTRIB | 元数据被改变,例如权限、时间戳、扩展属性、链接数、UID、GID等 |
IN_CLOSE_WRITE | 打开用于写的文件被关闭 |
IN_CREATE | 在监控的目录中创建了文件或目录 |
IN_DELETE | 在监控的目录中删除了文件或目录 |
IN_DELETE_SELF | 监控的文件或目录本身被删除 |
IN_CLOSE_NOWRITE | 不是打开用于写的文件被关闭 |
IN_MODIFY | 文件被修改 |
IN_MOVE_SELF | 监控的文件或目录本身被移动 |
IN_MOVED_FROM | 从监控的目录中移出文件 |
IN_MOVED_TO | 向监控的目录中移入文件 |
IN_OPEN | 文件或目录被打开 |
IN_ALL_EVENTS | 包含了上面提到的所有事件 |
- 移除监控对象
#include <sys/inotify.h> int inotify_rm_watch(int fd, int wd); ## 参数值 - 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; } // 创建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; } // 添加监听对象 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([&]() { // 循环监听事件 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) { // 仅作事件循环 sleep(2); } inotify_rm_watch(inotifyId, watchFd); close(epfd); close(inotifyId); return 0; }
- 执行结果
[root@localhost inotify]# ./testInotify
file create
dir create
file create
file modify
file create
file modify
dir delete
file delete
到此这篇关于C++利用inotify+epoll实现异步文件监控的方法的文章就介绍到这了,更多相关C++ inotify+epoll异步文件监控内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!