1 前言
对于linux系统的文件,如果需要监控其文件状态是否出现了增、删、改,在2.6.13或更新的linux kenerl版本上,我们可以用linux系统中提供的inotify
api 来实现
详细的api说明可以参考: man-pages
2 应用实现
2.1 主要逻辑
关键的函数有三个
inotify_init() inotify_add_watch() inotify_rm_watch()
我们直接从代码实现来看api的使用
1.初始化inotify并得到文件描述符fd
int fd = inotify_init(); if (fd == -1) { std::cerr << "Failed to initialize inotify" << std::endl; return; }
2.添加需要监控的文件/文件夹路径 得到监控文件描述符wd
#define INOTIFY_FOLDER_MASK (IN_CREATE | IN_MODIFY | IN_DELETE) std::string path = "/home/xx/"; // 可以是一个目录 // std::string path = "/home/xx/test.txt" // 也可以是一个文件 int wd = inotify_add_watch(fd, path.c_str(), INOTIFY_FOLDER_MASK); if (wd == -1) { std::cerr << "Failed to add watch to [" << path << "]" << ", error:" + std::to_string(errno) << std::endl; return; }
3.通过read
等待inotify事件唤醒, 并处理inotify_event
char buf[1024]; int len = read(fd, buf, sizeof(buf)); if (len == -1) { if (errno == EINTR) { std::cerr << "Interrupted by signal" << std::endl; } break; } std::cout << "read ok, len:" << len << std::endl; for (int i = 0; i < len;) { struct inotify_event *event = (struct inotify_event *)&buf[i]; i += sizeof(struct inotify_event) + event->len; if (event->mask & IN_CREATE) { std::cout << "File created: " << event->name << std::endl; } else if (event->mask & IN_MODIFY) { std::cout << "File modified: " << event->name << std::endl; } else if (event->mask & IN_DELETE) { std::cout << "File deleted: " << event->name << std::endl; } }
4.inotify_rm_watch
移除监控
if (fd == -1) { return; } if (wd != -1) { inotify_rm_watch(fd, wd); } close(fd);
2.2 注意事项
在实际场景中,我们通过step3拿到了inotify_event
,如何判断这是否是我们监控的目标文件,这需要根据step2中监控的path本身是文件还是目录来针对性处理
2.2.1 inotify path为文件的处理
检查event中的wd是否和step2中拿到的一致
event->wd == wd
注意这种情况下拿到的event->name
实际上是空的
2.2.2 inotify path为目录的处理
检查event->name
event->name == targetName // targetName即为你这个目录中你要看的文件名 例如test.txt
2.3 逻辑优化
实际业务中可以把这些处理封装到一个FileMonitor
类当中,对于变动文件的后处理也可以作为std::functional
的Callback
灵活添加实现
enum MONI_TYPE { MONI_FILE, MONI_FOLDER, MONI_END, }; class FileMonitor { public: FileMonitor(); FileMonitor(std::string str); ~FileMonitor(); void monitor(); void registerHandle(std::function<void(std::string)>); void setTargetFileName(std::string name); static void stopRun(); private: int fd; int wd; std::string path; std::string targetfileName; MONI_TYPE moniType; static bool run; void init(); void exit(); void checkType(); void addDeviceInotify(); bool match(std::string, int); std::string getRealPath(); std::function<void(std::string)> callBack; };
自定义Callback, 例如发现目标文件变动后我需要读取它
void CallBack(std::string str) { std::string line; std::ifstream ifs(str); if (!ifs.good()) { std::cerr << "Failed to open file: " << str << std::endl; return; } // just read first for test if (std::getline(ifs, line)) { std::cout << "Read line: " << line << std::endl; } } int main() { std::string pathStr = "/home/xx/"; FileMonitor monitor(pathStr); monitor.registerHandle(CallBack); }
文件变动后调用Callback
if (event->mask & IN_MODIFY) { std::cout << "File modified: " << event->name << std::endl; if (event->wd == wd) { if (callBack) { callBack(path); } else { std::cerr << "No handle function registered" << std::endl; } } }