文件系统事件监听

文件系统事件和网络IO事件一样,也可以通过epoll或者IOCP 事件管理器统一调度,当所监控的文件或文件夹发生了增删改的事件时,就会触发事件回调,进行事件处理。很常见的应用,如配置文件立即生效功能,就可以通过这种方式触发软件内部进行配置更新。也可以用来监控日志文件的更新,拉取最新日志(类似notepad++打开日志文件,如果日志文件更新了,就会提示reload)。

下面分别给出了linux和windows系统下文件系统事件监听的示意代码,仅供参考。

//file: FileMonitor.h#ifndef INOTIFY_FILEMONITOR_H
#define INOTIFY_FILEMONITOR_H#include <string>
#include <map>/*** 文件监视器。* 用于监控文件系统中文件相关的事件,如创建、删除等操作。*/
class FileMonitor {public:FileMonitor() = default;~FileMonitor() = default;void startWatch(const std::string &path);void stopWatch();bool addWatch(const std::string &path);private:// inotify 句柄int inotify_fd{0};// inotify_add_watch返回值 -> 要监控的文件名 的映射std::map<int, std::string> watchDesc2Name;// 是否停止监控bool isStopped{false};
};#endif //INOTIFY_FILEMONITOR_H// file: FileMonitor.cpp#include <sys/inotify.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cstdio>
#include <iostream>
#include "FileMonitor.h"#define log(format, ...) { \auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());   \char buf[64];                                                                        \strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&now));               \printf("%s | %llu | ", buf, std::this_thread::get_id());                             \printf(format, ##__VA_ARGS__);                                                       \printf("\n"); }#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )void FileMonitor::startWatch(const std::string &path) {log("Ready to watch...");// inotify 初始化inotify_fd = inotify_init();if (!addWatch(path)) {return;}// 创建一个 epoll 句柄log("epoll_create");int epfd = epoll_create(256);struct epoll_event ev;ev.data.fd = inotify_fd;        // 设置要处理的事件相关的文件描述符ev.events = EPOLLIN | EPOLLET;  // 设置要处理的事件类型// 注册 epoll 事件log("epoll_ctl");epoll_ctl(epfd, EPOLL_CTL_ADD, inotify_fd, &ev);// 循环监听事件char buffer[BUF_LEN];struct epoll_event events[20];  // 存储从内核得到的事件集合while (!isStopped) {log("epoll_wait...");// 等待事件发生。返回需要处理的事件数目int nfds = epoll_wait(epfd, events, 20, 500);log("Event count: %d", nfds);for (int i = 0; i < nfds; ++i) {/*** epoll_wait 会一直阻塞直到下面2种情况:*   1. 一个文件描述符触发了事件。*   2. 被一个信号处理函数打断,或者 timeout 超时。* 所以下面需要对 fd 进行过滤,判断是否是我们需要的 fd 产生了事件*/if (events[i].data.fd != inotify_fd) {log("Other event, fd=%d", events[i].data.fd);continue;}long length = read(inotify_fd, buffer, BUF_LEN);if (length < 0) {perror("Read failed");}log("Read length: %ld", length);int pos = 0;int count = 0;while (pos < length) {struct inotify_event *event = (struct inotify_event *) &buffer[pos];log("[%d]event->len=%u, event->mask=%#X, EVENT_SIZE=%lu", ++count, event->len, event->mask, EVENT_SIZE);if (event->len) {if (event->mask & IN_CREATE) {log("CREATE: %s", event->name);} else if (event->mask & IN_DELETE) {log("DELETE: %s", event->name);} else if (event->mask & IN_MODIFY) {log("MODIFY: %s", event->name);} else if (event->mask & IN_OPEN) {log("OPEN: %s", event->name);} else if (event->mask & IN_CLOSE) {log("CLOSE: %s", event->name);} else {log("Unknown event, mask=%u", event->mask);}}pos += EVENT_SIZE + event->len;}}}log("Stop inotify");for (auto &ele: watchDesc2Name) {inotify_rm_watch(inotify_fd, ele.first);}close(epfd);close(inotify_fd);
}void FileMonitor::stopWatch() {isStopped = true;
}bool FileMonitor::addWatch(const std::string &path) {// TODO 需要对文件(夹)是否存在进行判断// 监听指定目录下的修改、创建、删除事件int wd = inotify_add_watch(inotify_fd, path.c_str(), IN_MODIFY | IN_CREATE | IN_DELETE);
//    int wd = inotify_add_watch(inotify_fd, path.c_str(), IN_ALL_EVENTS);if (wd < 0) {log("inotify_add_watch failed: %s", path.c_str());return false;}log("Add watch: %s", path.c_str());watchDesc2Name[wd] = path;return true;
}// main: main.cpp
#include <iostream>
#include "FileMonitor.h"int main() {std::string dirPath = "/home/nvidia/logs";FileMonitor fileMonitor;fileMonitor.startWatch(dirPath);return 0;
}// https://github.com/BornToDeath/cpp/blob/main/test/case11_inotify/main.cpp
#include <iostream>
#include <map>
#include <windows.h>
#define DIRECTORY_PATH      L"C://TestFolder"#define CONTAINING_RECORD(address, type, field) ((type *)( \(PCHAR)(address) - \(ULONG_PTR)(&((type *)0)->field)))typedef struct directory_info
{HANDLE    hDir;CHAR          szDirName[MAX_PATH];CHAR          szBuffer[4096];DWORD      dwBufLength;OVERLAPPED Overlapped;  // 此处需要定义overlapp结构
}DIRECTORY_INFO, * LPDIRECTORY_INFO;class cFileUpdateChecker
{
public:cFileUpdateChecker(const wchar_t* root) :hThread(NULL), dwThread(0), hComp(NULL), pdir(NULL) { wsprintf(dirPath,L"%s",root); }~cFileUpdateChecker(){if (hComp) {PostQueuedCompletionStatus(hComp, 0, NULL, NULL);CloseHandle(hComp);}if (hThread)        CloseHandle(hThread);if (pdir){CloseHandle(pdir->hDir);HeapFree(GetProcessHeap(), 0, pdir);}for(auto it = opened_files.begin();it!=opened_files.end();it++){if(it->second != NULL)CloseHandle(it->second);}}// 定义IOCP的工作线程static DWORD WINAPI  ThreadFunc(void* p){cFileUpdateChecker* pHost = (cFileUpdateChecker*)p;LPDIRECTORY_INFO lpdir = NULL;DWORD nbytes, errCode;LPOVERLAPPED lpOverlapped = NULL;PFILE_NOTIFY_INFORMATION fni = NULL;ULONG_PTR completion_key;DWORD wait = 1000;while (true){BOOL bRet = GetQueuedCompletionStatus(pHost->hComp, &nbytes, &completion_key, &lpOverlapped, wait);if (FALSE == bRet ){ errCode = GetLastError();if (errCode == WAIT_TIMEOUT){wait = INFINITE;continue;}elsebreak;}if (lpOverlapped == NULL){break;}else{lpdir = CONTAINING_RECORD(lpOverlapped, DIRECTORY_INFO, Overlapped);if (lpdir){fni = (PFILE_NOTIFY_INFORMATION)lpdir->szBuffer;int filename_length =fni->FileNameLength;wchar_t* root_dir = (wchar_t*)lpdir->szDirName;wchar_t* filename = &fni->FileName[0];switch (fni->Action){case FILE_ACTION_ADDED:wprintf(L"The file %s/%s was added to the directory.\r\n", root_dir, filename);break;case FILE_ACTION_REMOVED:wprintf(L"The file  %s/%s  was removed from the directory.\r\n", root_dir, filename);break;case FILE_ACTION_MODIFIED:{wchar_t file_path_buffer[MAX_PATH]={};wsprintf(file_path_buffer,L"%s\\%s",root_dir, filename);wprintf(L"The file %s was modified. This can be a change in the time stamp or attributes.\r\n", file_path_buffer);auto it = pHost->opened_files.find(file_path_buffer);if(it == pHost->opened_files.end()){HANDLE hFile = CreateFile(file_path_buffer,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(hFile != NULL){DWORD dwFilePointer = SetFilePointer(hFile,0,NULL,FILE_END);if(dwFilePointer != INVALID_SET_FILE_POINTER)pHost->opened_files[file_path_buffer]=hFile;elseCloseHandle(hFile);}}else{CHAR szReadText[50] = {};DWORD dwRead = 0;//需要注意的是,FILE_ACTION_MODIFIED信号仅表示文件系统中的该文件节点对象的写状态更新了,至于是否完成落盘,还需要看修改文件的进程是否完成了刷盘。由于实时性问题,刷盘动作可能存在一定的滞后,如果每次进入Modify事件都要增量读取更新的文件内容。可以增加30~50ms的延时bRet = ReadFile(it->second,szReadText,32,&dwRead,NULL);if(bRet == FALSE){CloseHandle(it->second);pHost->opened_files.erase(it);}else{printf("Additional file content: %s.\r\n",szReadText);SetFilePointer(it->second,0,NULL,FILE_END);}}}break;case FILE_ACTION_RENAMED_OLD_NAME:{const wchar_t* new_filename = L"";int new_filename_length = 0;if (fni->NextEntryOffset);{PFILE_NOTIFY_INFORMATION new_fni = (PFILE_NOTIFY_INFORMATION)(lpdir->szBuffer + fni->NextEntryOffset);new_filename = &new_fni->FileName[0];new_filename_length = new_fni->FileNameLength;}wprintf(L"The file  %s/%s  was renamed, and this is the new name %s.\r\n", root_dir, filename, new_filename);}break;case FILE_ACTION_RENAMED_NEW_NAME:wprintf(L"The file  %s/%s  was renamed and this is the new name.\r\n", root_dir,  filename);break;default:break;}// re post eventZeroMemory(&(pHost->pdir->Overlapped), sizeof(OVERLAPPED));ZeroMemory(pHost->pdir->szBuffer, 4096);ReadDirectoryChangesW(pHost->pdir->hDir,pHost->pdir->szBuffer,sizeof(pHost->pdir->szBuffer),TRUE,FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_DIR_NAME |FILE_NOTIFY_CHANGE_ATTRIBUTES |FILE_NOTIFY_CHANGE_SIZE |FILE_NOTIFY_CHANGE_LAST_ACCESS |FILE_NOTIFY_CHANGE_CREATION |FILE_NOTIFY_CHANGE_SECURITY |FILE_NOTIFY_CHANGE_LAST_WRITE,&(pHost->pdir->dwBufLength),(OVERLAPPED*)(&(pHost->pdir->Overlapped)),NULL);}}}return 0;}bool StartChecker(){hComp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);pdir = (LPDIRECTORY_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DIRECTORY_INFO));if (NULL == pdir){return false;}pdir->hDir = CreateFile(dirPath,FILE_LIST_DIRECTORY,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,NULL);if (pdir->hDir == INVALID_HANDLE_VALUE){return false;}// 启动工作线程hThread = CreateThread(NULL, 0, ThreadFunc, this, 0, &dwThread);if (NULL == hThread){return false;}ZeroMemory(&(pdir->Overlapped), sizeof(OVERLAPPED));ZeroMemory(pdir->szBuffer, 4096);ZeroMemory(pdir->szDirName, MAX_PATH);wsprintf((wchar_t*)pdir->szDirName,L"%s", dirPath);// 投递I/O请求到完成端口HANDLE hRet = CreateIoCompletionPort(pdir->hDir, hComp,NULL, 0);if (NULL == hComp){return false;}BOOL bRet = ReadDirectoryChangesW(pdir->hDir,pdir->szBuffer,4096,TRUE,FILE_NOTIFY_CHANGE_FILE_NAME |FILE_NOTIFY_CHANGE_DIR_NAME |FILE_NOTIFY_CHANGE_ATTRIBUTES |FILE_NOTIFY_CHANGE_SIZE |FILE_NOTIFY_CHANGE_LAST_ACCESS |FILE_NOTIFY_CHANGE_CREATION |FILE_NOTIFY_CHANGE_SECURITY |FILE_NOTIFY_CHANGE_LAST_WRITE,&pdir->dwBufLength,&(pdir->Overlapped),NULL);if (FALSE == bRet){return false;}return true;}
private:HANDLE hThread;DWORD dwThread;HANDLE  hComp;LPDIRECTORY_INFO pdir;WCHAR dirPath[MAX_PATH];std::map<std::wstring,HANDLE> opened_files;
};int main()
{cFileUpdateChecker* ckr = new cFileUpdateChecker(DIRECTORY_PATH);std::cout << "Supervise "; std::wcout << DIRECTORY_PATH << " Begin..." << std::endl;ckr->StartChecker();getchar();delete ckr;getchar();
}// https://blog.csdn.net/nhn_devlab/article/details/6034055

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/735910.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot自定义banner,自定义logo

SpringBoot自定义banner&#xff0c;自定义logo 在线网站 http://www.network-science.de/ascii/?spma2c6h.12873639.article-detail.9.7acc2c9aSTnQdW https://www.bootschool.net/ascii?spma2c6h.12873639.article-detail.8.7acc2c9aSTnQdW https://patorjk.com/softwa…

官方安装配置要求服务器最低2核4G

官方安装配置要求服务器至少2核、4G。 如果服务器低于这个要求&#xff0c;就没有必要安装&#xff0c;因为用户体验超级差。 对于服务器CPU来说&#xff0c;建议2到4核就完全足够了&#xff0c;太多就浪费了&#xff0c;但是内存越大越好&#xff0c;最好是4G以上。 如果服务器…

创建Django项目,实现视图,路由

初识Django 1、创建Django项目 Django项目的创建的路径不要有中文和空格&#xff1b;【计算机名称不要是中文】 1、在cmd中命令进行创建Django项目打开存放项目的位置创建Django项目&#xff1a;django-admin startproject 项目名称(注意&#xff1a;项目名称不要是中文)启动…

贝叶斯优化的门控循环神经网络BO-GRU(时序预测)的Matlab实现

贝叶斯优化的门控循环神经网络&#xff08;BO-GRU&#xff09;是一种结合了贝叶斯优化&#xff08;Bayesian Optimization, BO&#xff09;和门控循环单元&#xff08;Gated Recurrent Unit, GRU&#xff09;的模型&#xff0c;旨在进行时序预测。这种模型特别适用于时间序列数…

计算机网络概述(接入网和物理媒体)

一、接入网和物理媒体 将终端系统连接到边缘路由器的途径&#xff1a; 住宅接入网、机构接入网络、移动接入网络。 1.接入网:家庭网络 &#xff08;1&#xff09;Dial-up&#xff08;拨号&#xff09; Modem 将上网数据调制加兹安音频信号上在电话线是哪个传输&#xff0c;在…

VBA(学习笔记)

1. 数据类型 变量定义&#xff1a;Dim 变量名 As 数据类型 变量赋值&#xff1a;变量名 值 1.1 数值型 1.1.1 整数 (1) Byte&#xff1a;1字节&#xff08;0~255&#xff09; (2) Integer&#xff1a;2字节&#xff08;-32768~32767&#xff09; (3) Long&#xff1a;4…

【网络应用层协议】【MQTT】详解消息队列遥测传输协议MQTT(超详细)

目录 1. MQTT 协议简介 2. MQTT 的特点 3. MQTT 协议原理 4. MQTT协议中的订阅、主题、会话 1. MQTT 协议简介 MQTT&#xff08; Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议 &#xff09;是一种消息列队传输协议&#xff0c;采用订阅、发布机制&…

arcgis栅格数据处理3——定义投影(同样适用于其他类型文件)

进行数据连接时可能出现未设置投影无法链接的情况&#xff0c;需要先定义投影 点击最右侧“目录”&#xff0c;弹出带有系统工具的面板&#xff0c;点击“data management tools”点击“投影”&#xff0c;“定义投影”

Redis的缓存穿透?缓存击穿?缓存雪崩?

缓存穿透 什么是缓存穿透&#xff1f; 假如我们有一个学生表一共有10条数据&#xff0c;对应的id为1-10。我们有一个请求是通过id去查询学生的信息。正常的流程是请求先到redis里面去找&#xff0c;如果命中就将查询到的结果反回&#xff0c;如果没有就去mysql数据库中找&…

chatgpt与人类有何不同?

ChatGPT和人类之间存在多个显著的差异。 首先&#xff0c;ChatGPT是一种基于人工智能技术的计算机程序&#xff0c;通过机器学习和自然语言处理等技术&#xff0c;从大量的数据中获取知识并生成语言输出。它主要依赖于算法和数据进行工作&#xff0c;能够迅速处理和检索信息&a…

OpenCV(六)——对于图片的算术运行、图片的叠加操作、多通道图像拆分为多个单通道图像

在OpenCV中&#xff0c;可以对于图片进行算法运算。我们知道&#xff0c;图像的本质其实就是矩阵&#xff0c;因此对于图像的算数运算本质上就是对于矩阵的算术运算。在OpenCV可以对图像进行算术运算的操作有加、减、乘、除等操作。 图像的加、减、乘、除操作 两张图像可以进…

微信小程序开发系列(二十六)·小程序运行机制(启动、前后台状态、挂起、销毁)和小程序更新机制

目录 1. 小程序运行机制 1.1 启动 1.2 前台和后台状态 1.3 挂起 1.4 销毁 2. 小程序更新机制 1. 小程序运行机制 1.1 启动 小程序启动可以分为两种情况&#xff0c;一种是冷启动&#xff0c;一种是热启动。 冷启动&#xff1a;如果用户首次打开&#xff0c;或小…

数据挖掘案例分析、经典案例、技术实现方案

1.数据挖掘案例分析 数据挖掘&#xff1a;是从大量数据中发现有用信息和模式的过程。 数据挖掘案例分析&#xff1a;是指通过对已有数据进行挖掘和分析&#xff0c;发现其中的模式和规律&#xff0c;并根据这些发现提出相应的解决方案和决策建议的过程。 以下是一些常见的数据…

Java中交集、并集、差集、补集

一、交集 //交集Testpublic void intersection(){//向集合中添加元素ArrayList<String> datas new ArrayList<>();Collections.addAll(datas, "张三", "李四", "王五", "赵六");//向集合中添加元素ArrayList<String&…

03-快速上手RabbitMQ的5种消息模型

RabbitMQ RabbitMQ是基于Erlang语言开发的开源消息通信中间件,有几个常见概念 connections(连接): 将来publisher(消息的发送者)或者consumer(消息的接收者)都需要先与MQ建立连接 channel(通道): 建立连接后需要创建通道,生产者和消费者就是基于通道完成消息的发送和接收 ex…

django自定义表单

文章目录 django自定义表单两种方式自定义错误信息widgets实例化和初始化处理用户提交的数据验证表单通用类试图使用表单Formset django自定义表单 两种方式 继承Form类继承ModelForm类 from django import forms from .models import Contact# 自定义表单字段 class Contac…

_note_06

1.说一说函数的按地址传递和按值传递&#xff0c;他们的区别是什么&#xff1f; 函数的参数传递方式可以分为按地址传递&#xff08;也称为按引用传递&#xff09;和按值传递两种方式。按值传递是指将实际参数的值复制给形式参数&#xff0c;即在函数调用时&#xff0c;实际参数…

探索云原生数据库技术:构建高效可靠的云原生应用

数据库是应用开发中非常重要的组成部分&#xff0c;可以进行数据的存储和管理。随着企业业务向数字化、在线化和智能化的演进过程中&#xff0c;面对指数级递增的海量存储需求和挑战以及业务带来的更多的热点事件、突发流量的挑战&#xff0c;传统的数据库已经很难满足和响应快…

2023年终总结——跌跌撞撞不断修正

目录 一、回顾1.一月&#xff0c;鼓足信心的开始2.二月&#xff0c;焦躁不安3.三月&#xff0c;路还是要一步一步的走4.四月&#xff0c;平平淡淡的前行5.五月&#xff0c;轰轰烈烈的前行6.六月&#xff0c;看事情更底层透彻了7.七月&#xff0c;设计模式升华月8.八月&#xff…

04hive数仓内外部表复杂数据类型与分区分桶

hive内部表和外部表 默认为内部表&#xff0c;外部表的关键字 &#xff1a;external内部表&#xff1a;对应的文件夹就在默认路径下 /user/hive/warehouse/库名.db/外部表&#xff1a;数据文件在哪里都行&#xff0c;无须移动数据 # students.txt 1,Lucy,girl,23 2,Tom,boy,2…