C++利用inotify+epoll实现异步文件监控的方法

 更新时间:2023年08月02日 09:18:26   作者:却道天凉_好个秋  
这篇文章讲给大家详细介绍一下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异步文件监控内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++的get()函数与getline()函数使用详解

    C++的get()函数与getline()函数使用详解

    这篇文章主要介绍了C++的get()函数与getline()函数使用详解,是C++入门学习中的基础知识,需要的朋友可以参考下
    2015-09-09
  • C++实现文件逐行读取与字符匹配的示例详解

    C++实现文件逐行读取与字符匹配的示例详解

    这篇文章主要为大家详细介绍了如何溧阳C++实现文件逐行读取与字符匹配的功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下
    2023-03-03
  • C++实现字符格式相互转换的示例代码

    C++实现字符格式相互转换的示例代码

    这篇文章主要为大家详细介绍了C++中实现字符格式相互转换的方法,主要有UTF8与string互转、wstring与string互转,感兴趣的小伙伴可以了解一下
    2022-11-11
  • Eclipse中C++连接mysql数据库

    Eclipse中C++连接mysql数据库

    这篇文章主要为大家详细介绍了Eclipse中C++连接mysql数据库 ,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • C语言 超详细介绍与实现线性表中的带头双向循环链表

    C语言 超详细介绍与实现线性表中的带头双向循环链表

    带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单
    2022-03-03
  • 海量数据处理系列之:用C++实现Bitmap算法

    海量数据处理系列之:用C++实现Bitmap算法

    本篇文章是对用C++实现Bitmap算法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • OpenCV实现单目尺寸估计的案例详解

    OpenCV实现单目尺寸估计的案例详解

    这篇文章主要介绍了通过OpenCV如何实现单目尺寸估计,文中的示例代码讲解详细,对我们学习和工作有一定的参考价值,感兴趣的可以了解一下
    2022-01-01
  • VSCode插件开发全攻略之打包、发布、升级的详细教程

    VSCode插件开发全攻略之打包、发布、升级的详细教程

    这篇文章主要介绍了VSCode插件开发全攻略之打包、发布、升级的教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • C++中关于getchar()的使用方法

    C++中关于getchar()的使用方法

    这篇文章主要介绍了C++中关于getchar()的使用方法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • C++最优二叉树哈夫曼树算法解析

    C++最优二叉树哈夫曼树算法解析

    这篇文章主要介绍了C++最优二叉树哈夫曼树算法解析,哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树,所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度,需要的朋友可以参考下
    2023-08-08

最新评论