如何通过inotify获取被监控文件的新增数据(避免全量读取)
问题
我正在用inotify监控文件内容变化,希望只获取新增的内容,但不知道怎么计算文件中新增的字节数。能不能通过inotify实现?想要一种替代方案,避免读取整个文件、和内存中之前的文件内容对比生成差异的方式,因为文件会随时间变大。
附上的代码如下:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <sys/inotify.h> #include <unistd.h> #include <fcntl.h> void watchFile(char* filePath) { // Create an inotify instance int fd = inotify_init(); if (fd == -1) { perror("inotify_init"); return; } // Add the file to the watch list int wd = inotify_add_watch(fd, filePath, IN_MODIFY); if (wd == -1) { perror("inotify_add_watch"); return; } // Set up a buffer to hold incoming events char buffer[4096]; while (true) { // Wait for events int length = read(fd, buffer, sizeof(buffer)); if (length == -1) { perror("read"); return; } // Parse the events int offset = 0; while (offset < length) { struct inotify_event* event = (struct inotify_event*) &buffer[offset]; if (event->len) { // Seek to the end of the file int file = open(filePath, O_RDONLY); lseek(file, 0, SEEK_END); // Read the new data from the file char newData[???] = {0}; // how do i calculate this? read(file, &newData[0], ??); // Handle the new data printf("New data: %s\n", newData); close(file); } offset += sizeof(struct inotify_event) + event->len; } } // Stop watching the file inotify_rm_watch(fd, wd); // Close the inotify instance close(fd); } int main() { watchFile("path/to/file.txt"); return 0; }
期望效果:编译后的程序./a.out,当向path/to/file.txt写入baa、foo、hehe等内容时,输出:
New data: baa New data: foo New data: hehe
解决方案
inotify本身不会直接返回新增内容的字节数,但可以通过记录文件读取偏移量的方式实现只读取新增部分,完全不需要读取整个文件或对比旧内容。
核心思路
- 程序启动时获取文件初始大小,作为后续读取的起始偏移量
- 每次收到
IN_MODIFY事件时,计算当前文件大小与上次偏移量的差值,这个差值就是新增内容的长度 - 从上次偏移量位置开始读取对应长度的内容,读取完成后更新偏移量为当前文件大小
- 额外监控
IN_TRUNCATE事件,处理文件被截断(比如清空)的情况,此时重置偏移量为0
修改后的代码
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <sys/inotify.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> void watchFile(char* filePath) { int fd = inotify_init(); if (fd == -1) { perror("inotify_init"); return; } // 同时监控文件修改和截断事件,处理文件被清空的场景 int wd = inotify_add_watch(fd, filePath, IN_MODIFY | IN_TRUNCATE); if (wd == -1) { perror("inotify_add_watch"); close(fd); return; } // 获取文件初始大小,作为读取起始偏移量 off_t current_offset = 0; struct stat file_stat; if (stat(filePath, &file_stat) == 0) { current_offset = file_stat.st_size; } char event_buffer[4096]; char read_buffer[1024]; // 可根据实际需求调整缓冲区大小 while (true) { int length = read(fd, event_buffer, sizeof(event_buffer)); if (length == -1) { perror("read"); break; } int offset = 0; while (offset < length) { struct inotify_event* event = (struct inotify_event*) &event_buffer[offset]; // 处理文件截断事件,重置偏移量到文件开头 if (event->mask & IN_TRUNCATE) { current_offset = 0; } // 处理文件修改事件,读取新增内容 else if (event->mask & IN_MODIFY) { int file_fd = open(filePath, O_RDONLY); if (file_fd == -1) { perror("open"); offset += sizeof(struct inotify_event) + event->len; continue; } // 获取当前文件大小 if (fstat(file_fd, &file_stat) == -1) { perror("fstat"); close(file_fd); offset += sizeof(struct inotify_event) + event->len; continue; } off_t file_size = file_stat.st_size; if (file_size > current_offset) { // 移动到上次读取结束的位置 lseek(file_fd, current_offset, SEEK_SET); // 循环读取新增内容,避免缓冲区不足 ssize_t bytes_read; while ((bytes_read = read(file_fd, read_buffer, sizeof(read_buffer) - 1)) > 0) { read_buffer[bytes_read] = '\0'; printf("New data: %s", read_buffer); current_offset += bytes_read; } } else if (file_size < current_offset) { // 处理未触发IN_TRUNCATE的截断场景,重置偏移量 current_offset = file_size; } close(file_fd); } offset += sizeof(struct inotify_event) + event->len; } } inotify_rm_watch(fd, wd); close(fd); } int main() { watchFile("path/to/file.txt"); return 0; }
代码关键点说明
- 偏移量维护:通过
current_offset记录上次读取到的文件位置,确保每次只读取新增部分 - 事件扩展:新增
IN_TRUNCATE监控,处理文件被清空的场景,避免后续读取错误 - 分段读取:用循环读取适配大段新增内容,防止缓冲区不足导致内容丢失
- 性能优化:全程只读取新增内容,无需加载整个文件,完美适配大文件场景
内容的提问来源于stack exchange,提问作者Coder
相关产品推荐
相关产品推荐

