文件系统事件监听
文件系统事件和网络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
相关文章:
文件系统事件监听
文件系统事件和网络IO事件一样,也可以通过epoll或者IOCP 事件管理器统一调度,当所监控的文件或文件夹发生了增删改的事件时,就会触发事件回调,进行事件处理。很常见的应用,如配置文件立即生效功能,就可以通…...
探秘HTTPS:如何通过SSL/TLS保证网络通信安全
目录 引言 详解HTTPS加密实现机制 SSL/TLS工作原理 结论 引言 随着网络安全威胁的日益增加,HTTPS通过SSL(Secure Sockets Layer)和TLS(Transport Layer Security)协议提供的加密技术变得至关重要。这些技术保证了用…...
Java算法之动态规划
Java算法之动态规划 前言 最近这一段时间一直在刷算法题,基本上一有时间就会做一两道,这两天做了几道动态规划的问题,动态规划之前一直是我比较头疼的一个问题,感觉好复杂,一遇到这样的问题就想跳过,昨…...
C++从零开始的打怪升级之路(day47)
这是关于一个普通双非本科大一学生的C的学习记录贴 在此前,我学了一点点C语言还有简单的数据结构,如果有小伙伴想和我一起学习的,可以私信我交流分享学习资料 那么开启正题 今天分享的是关于set和map的知识点 1.关联式容器 在前面&#…...
香橙派AIpro开发板开箱测评
2023年12月,香橙派联合华为发布了基于昇腾的Orange Pi AIpro开发板,提供8/20TOPS澎湃算力,能覆盖生态开发板者的主流应用场景,让用户实践各种创新场景,并为其提供配套的软硬件。香橙派AIpro开发板一经发布便吸引了众多…...
ISP基础概述
原文来自ISP 和摄像头基本知识 本文主要介绍ISP,以供读者能够理解该技术的定义、原理、应用。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:计算机杂记 🎀CSDN主页 发狂的小花 dz…...
C++第一弹---C++入门(上)
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 【C详解】 C入门 1、C关键字(C98) 2、命名空间 2.1、命名空间定义 2.2、命名空间使用 3、C输入&输出 4、缺省参数 4.1、缺省参数概念 4.2、缺省参…...
VScode格式化快捷键
vscode格式化代码快捷键 如何使用快捷键格式化代码。使用Java的格式去设置,发现不起作用。 在这里记录一下: 在Windows中,vscode格式化代码快捷键是“ShiftAltF”; 在Mac中,vscode格式化代码快捷键是“ShiftOption…...
HCIP---IS-IS协议
文章目录 前言一、pandas是什么?二、使用步骤 1.引入库2.读入数据总结 一.IS-IS协议概述 IS-IS是一种基于链路状态的内部网关协议(IGP),它使用最短路径优先算法(SPF或Dijkstra)进行路由计算。这种协议在自治…...
突破编程_C++_设计模式(组合模式)
1 组合模式的基本概念 C中的组合模式是一种对象结构型模式,它将多个对象组合成树形结构,以表示具有整体-部分关系的层次结构。在这个模式中,对单个对象(叶子对象)与组合对象(容器对象)的使用具…...
010Editor汉化版+下载+注册码+模板bug
项目场景: 这天我想使用我的不知名的一个破解版本的010Edit来查看一个EXE程序,并想使用模板功能,但是发现没有该模板还无法下载最新模板 问题描述 010Edit联网后需要注册码: 010 Editor 激活码生成器 使用方法 参照教程使用0…...
js【详解】BOM
浏览器对象模型 (Browser obiect Mode 简称 BOM) 浏览器对象即 window,调用window对象的属性和方法时,可以省略window window 常用的属性 Navigator 常用于获取浏览器的信息 navigator.userAgent;火狐浏览器范例: “…...
Leetcode 3077. Maximum Strength of K Disjoint Subarrays
Leetcode 3077. Maximum Strength of K Disjoint Subarrays 1. 解题思路 1. 朴素思路2. 算法优化 2. 代码实现 题目链接:3077. Maximum Strength of K Disjoint Subarrays 1. 解题思路 这道题很惭愧没有搞定,思路上出现了差错,导致一直没能…...
【JetsonNano】onnxruntime-gpu 环境编译和安装,支持 Python 和 C++ 开发
1. 设备 2. 环境 sudo apt-get install protobuf-compiler libprotoc-devexport PATH/usr/local/cuda/bin:${PATH} export CUDA_PATH/usr/local/cuda export cuDNN_PATH/usr/lib/aarch64-linux-gnu export CMAKE_ARGS"-DONNX_CUSTOM_PROTOC_EXECUTABLE/usr/bin/protoc&qu…...
知名比特币质押协议项目Babylon确认参加Hack.Summit()2024区块链开发者大会
Babylon项目已确认将派遣其项目代表出席2024年在香港数码港举办的Hack.Summit()2024区块链开发者大会。作为比特币生态的领军项目,Babylon积极参与全球区块链领域的交流与合作,此次出席大会将为其提供一个展示项目进展、交流技术与创新思路的重要平台。B…...
如何学习、上手点云算法(三):用VsCode、Visual Studio来debug基于PCL、Open3D的代码
写在前面 本文内容 以PCL 1.14.0,Open3D0.14.1为例,对基于PCL、Open3D开发的代码进行源码debug; 如何学习、上手点云算法系列: 如何学习、上手点云算法(一):点云基础 如何学习、上手点云算法(二):点云处理相…...
【干货】alzet渗透泵操作说明
alzet渗透泵是一款小型、可植入式的胶囊渗透泵产品,此产品由于其独特的渗透原理,深受广大科研人员的喜爱。该泵可适用于小鼠、大鼠及其他实验动物的研究,并且alzet渗透泵可减轻科研人员夜间及周末给药的困扰。alzet渗透泵无需外部连接或频繁处…...
CVPR 2022 Oral | Bailando: 基于编舞记忆和Actor-Critic GPT的3D舞蹈生成
目录 测试结果: 02 提出的方法 测试结果: 预测有3个步骤,速度比较慢 02 提出的方法 1. 针对舞蹈序列的VQ-VAE和编舞记忆 与之前的方法不同,我们不学习从音频特征到 3D 关键点序列的连续域的直接映射。相反,我们先让…...
解读电影级视频生成模型 MovieFactory
Diffusion Models视频生成-博客汇总 前言:MovieFactory是第一个全自动电影生成模型,可以根据用户输入的文本信息自动扩写剧本,并生成电影级视频。其中针对预训练的图像生成模型与视频模型之间的gap提出了微调方法非常值得借鉴。这篇博客详细解读一下这篇论文《MovieFactory:…...
【Python从入门到进阶】50、当当网Scrapy项目实战(三)
接上篇《49、当当网Scrapy项目实战(二)》 上一篇我们讲解了的Spider与item之间的关系,以及如何使用item,以及使用pipelines管道进行数据下载的操作,本篇我们来讲解Scrapy的多页面下载如何实现。 一、多页面下载原理分…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...
论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
