inotify_init 和 inotify_init1 函数详解 見出しへのリンク
1. 函数介绍 見出しへのリンク
inotify_init和inotify_init1函数是Linux系统中用于文件系统事件监控的函数。可以把它们想象成"文件系统监控摄像头",能够实时监控文件和目录的各种变化,如创建、删除、修改、访问等事件。
inotify是Linux 2.6.13内核引入的文件系统事件监控机制,它比传统的轮询方式更加高效和实时。当文件系统发生变化时,内核会立即通知监控程序,而不需要程序主动去检查文件状态。
inotify_init是基础版本,创建一个inotify实例并返回文件描述符。 inotify_init1是增强版本,支持额外的标志参数来控制inotify实例的行为。
使用场景:
- 文件同步工具(如rsync、syncthing)
- 实时日志监控
- 文件系统审计
- 开发工具的自动重新加载
- 系统监控和入侵检测
- 备份软件的增量备份
2. 函数原型 見出しへのリンク
#include <sys/inotify.h>
// 基础版本
int inotify_init(void);
// 增强版本
int inotify_init1(int flags);
3. 功能 見出しへのリンク
这两个函数的主要功能是创建一个inotify实例,用于监控文件系统事件:
- inotify_init: 创建标准的inotify实例
- inotify_init1: 创建支持额外选项的inotify实例
4. 参数 見出しへのリンク
inotify_init1参数: 見出しへのリンク
- flags: 控制标志
- 类型:int
- 含义:控制inotify实例行为的标志位
- 常用值:
- 0:默认行为(等同于inotify_init)
- IN_CLOEXEC:设置close-on-exec标志
- IN_NONBLOCK:设置非阻塞I/O模式
5. 返回值 見出しへのリンク
- 成功: 返回inotify实例的文件描述符(非负整数)
- 失败: 返回-1,并设置errno错误码
- EMFILE:进程文件描述符达到限制
- ENFILE:系统文件描述符达到限制
- ENOMEM:内存不足
- EINVAL:flags参数无效(inotify_init1)
6. 相似函数或关联函数 見出しへのリンク
- inotify_add_watch(): 添加监控目标
- inotify_rm_watch(): 移除监控目标
- read(): 读取事件数据
- close(): 关闭inotify实例
- poll(): 监控inotify文件描述符
- select(): 监控inotify文件描述符
- fanotify: 更高级的文件系统监控接口
7. 示例代码 見出しへのリンク
示例1:基础inotify使用 - 简单文件监控 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
// 解析和显示inotify事件
void display_inotify_event(struct inotify_event* event) {
time_t now = time(NULL);
printf("[%s", ctime(&now));
// 移除换行符
char* newline = strchr(ctime(&now), '\n');
if (newline) *newline = '\0';
printf("] ");
if (event->len) {
printf("文件 '%s' 发生事件: ", event->name);
} else {
printf("监控目标发生事件: ");
}
// 解析事件类型
if (event->mask & IN_ACCESS) printf("访问 ");
if (event->mask & IN_MODIFY) printf("修改 ");
if (event->mask & IN_ATTRIB) printf("属性改变 ");
if (event->mask & IN_CLOSE_WRITE) printf("写入后关闭 ");
if (event->mask & IN_CLOSE_NOWRITE) printf("只读后关闭 ");
if (event->mask & IN_OPEN) printf("打开 ");
if (event->mask & IN_MOVED_FROM) printf("移出 ");
if (event->mask & IN_MOVED_TO) printf("移入 ");
if (event->mask & IN_CREATE) printf("创建 ");
if (event->mask & IN_DELETE) printf("删除 ");
if (event->mask & IN_DELETE_SELF) printf("自我删除 ");
if (event->mask & IN_MOVE_SELF) printf("自我移动 ");
// 特殊事件
if (event->mask & IN_UNMOUNT) printf("[文件系统卸载] ");
if (event->mask & IN_Q_OVERFLOW) printf("[事件队列溢出] ");
if (event->mask & IN_IGNORED) printf("[监控被忽略] ");
printf("\n");
// 显示cookie(用于移动操作)
if (event->cookie > 0) {
printf(" 移动cookie: %u\n", event->cookie);
}
}
// 创建测试文件
int create_test_files(const char* directory) {
printf("创建测试文件...\n");
// 创建测试目录
if (mkdir(directory, 0755) == -1 && errno != EEXIST) {
perror("创建测试目录失败");
return -1;
}
// 创建一些测试文件
const char* filenames[] = {
"test1.txt", "test2.txt", "test3.txt", "document.pdf", "image.jpg"
};
for (int i = 0; i < 5; i++) {
char filepath[256];
snprintf(filepath, sizeof(filepath), "%s/%s", directory, filenames[i]);
int fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
const char* content = "这是测试文件内容\n";
write(fd, content, strlen(content));
close(fd);
printf("创建文件: %s\n", filepath);
}
}
return 0;
}
int main() {
printf("=== 基础inotify使用示例 ===\n");
const char* monitor_dir = "inotify_test_dir";
// 创建测试文件
if (create_test_files(monitor_dir) == -1) {
exit(EXIT_FAILURE);
}
// 初始化inotify
printf("\n1. 初始化inotify:\n");
int inotify_fd = inotify_init();
if (inotify_fd == -1) {
perror("inotify_init失败");
exit(EXIT_FAILURE);
}
printf("inotify文件描述符: %d\n", inotify_fd);
// 添加监控目标
printf("\n2. 添加监控目标:\n");
int watch_descriptor = inotify_add_watch(inotify_fd, monitor_dir,
IN_CREATE | IN_DELETE | IN_MODIFY |
IN_MOVED_FROM | IN_MOVED_TO |
IN_ACCESS | IN_ATTRIB);
if (watch_descriptor == -1) {
perror("添加监控目标失败");
close(inotify_fd);
exit(EXIT_FAILURE);
}
printf("监控描述符: %d\n", watch_descriptor);
printf("监控目录: %s\n", monitor_dir);
printf("监控事件: 创建、删除、修改、移动、访问、属性改变\n");
// 创建事件缓冲区
char buffer[BUF_LEN];
printf("\n3. 开始监控 (按Ctrl+C停止):\n");
printf("请在另一个终端中操作 %s 目录来测试监控功能\n", monitor_dir);
printf("例如: touch %s/newfile.txt\n", monitor_dir);
printf(" rm %s/test1.txt\n", monitor_dir);
printf(" echo 'test' > %s/test2.txt\n", monitor_dir);
// 监控循环
int event_count = 0;
while (event_count < 20) { // 限制事件数量以避免无限循环
// 读取事件(阻塞)
ssize_t bytes_read = read(inotify_fd, buffer, BUF_LEN);
if (bytes_read == -1) {
if (errno == EAGAIN || errno == EINTR) {
continue; // 被信号中断,继续等待
}
perror("读取inotify事件失败");
break;
}
// 解析事件
for (char* ptr = buffer; ptr < buffer + bytes_read; ) {
struct inotify_event* event = (struct inotify_event*)ptr;
display_inotify_event(event);
event_count++;
// 移动到下一个事件
ptr += EVENT_SIZE + event->len;
}
// 提示继续操作
if (event_count % 5 == 0) {
printf("(已处理 %d 个事件,继续操作或按Ctrl+C退出)\n", event_count);
}
}
// 清理资源
printf("\n4. 清理资源:\n");
if (inotify_rm_watch(inotify_fd, watch_descriptor) == -1) {
perror("移除监控失败");
} else {
printf("移除监控描述符: %d\n", watch_descriptor);
}
close(inotify_fd);
printf("关闭inotify文件描述符: %d\n", inotify_fd);
// 清理测试文件
printf("\n5. 清理测试文件:\n");
const char* test_files[] = {
"inotify_test_dir/test1.txt", "inotify_test_dir/test2.txt",
"inotify_test_dir/test3.txt", "inotify_test_dir/document.pdf",
"inotify_test_dir/image.jpg"
};
for (int i = 0; i < 5; i++) {
if (unlink(test_files[i]) == 0) {
printf("删除文件: %s\n", test_files[i]);
}
}
if (rmdir(monitor_dir) == 0) {
printf("删除目录: %s\n", monitor_dir);
}
printf("\n=== 基础inotify演示完成 ===\n");
return 0;
}
示例2:inotify_init1使用 - 高级监控选项 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
// 显示inotify实例信息
void show_inotify_info(int fd, const char* description) {
printf("inotify实例信息 (%s):\n", description);
printf(" 文件描述符: %d\n", fd);
// 获取文件状态
struct stat fd_stat;
if (fstat(fd, &fd_stat) == 0) {
printf(" 文件类型: %s\n", S_ISFIFO(fd_stat.st_mode) ? "FIFO" : "其他");
}
printf("\n");
}
// 非阻塞读取事件
int nonblocking_read_events(int inotify_fd) {
char buffer[BUF_LEN];
int event_count = 0;
// 非阻塞读取
ssize_t bytes_read = read(inotify_fd, buffer, BUF_LEN);
if (bytes_read > 0) {
// 解析事件
for (char* ptr = buffer; ptr < buffer + bytes_read; ) {
struct inotify_event* event = (struct inotify_event*)ptr;
time_t now = time(NULL);
printf("[%s", ctime(&now));
char* newline = strchr(ctime(&now), '\n');
if (newline) *newline = '\0';
printf("] 非阻塞读取事件: ");
if (event->len) {
printf("文件 '%s' ", event->name);
}
if (event->mask & IN_CREATE) printf("创建 ");
if (event->mask & IN_DELETE) printf("删除 ");
if (event->mask & IN_MODIFY) printf("修改 ");
printf("\n");
event_count++;
ptr += EVENT_SIZE + event->len;
}
} else if (bytes_read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("无事件待处理\n");
} else {
perror("读取事件失败");
}
}
return event_count;
}
// 使用poll监控inotify
int poll_inotify_events(int inotify_fd, int timeout_ms) {
struct pollfd pfd = {
.fd = inotify_fd,
.events = POLLIN,
.revents = 0
};
printf("使用poll等待事件 (超时: %d ms)...\n", timeout_ms);
int ret = poll(&pfd, 1, timeout_ms);
if (ret == -1) {
perror("poll失败");
return -1;
} else if (ret == 0) {
printf("poll超时,无事件\n");
return 0;
} else {
if (pfd.revents & POLLIN) {
printf("检测到inotify事件,读取中...\n");
return nonblocking_read_events(inotify_fd);
}
return 0;
}
}
int main() {
printf("=== inotify_init1高级选项示例 ===\n");
// 使用inotify_init1创建非阻塞实例
printf("1. 创建非阻塞inotify实例:\n");
int inotify_fd_nonblock = inotify_init1(IN_NONBLOCK);
if (inotify_fd_nonblock == -1) {
perror("inotify_init1(IN_NONBLOCK)失败");
exit(EXIT_FAILURE);
}
show_inotify_info(inotify_fd_nonblock, "非阻塞模式");
// 使用inotify_init1创建close-on-exec实例
printf("2. 创建close-on-exec inotify实例:\n");
int inotify_fd_cloexec = inotify_init1(IN_CLOEXEC);
if (inotify_fd_cloexec == -1) {
perror("inotify_init1(IN_CLOEXEC)失败");
close(inotify_fd_nonblock);
exit(EXIT_FAILURE);
}
show_inotify_info(inotify_fd_cloexec, "close-on-exec模式");
// 使用inotify_init1创建同时具有两个标志的实例
printf("3. 创建非阻塞且close-on-exec的inotify实例:\n");
int inotify_fd_both = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (inotify_fd_both == -1) {
perror("inotify_init1(IN_NONBLOCK | IN_CLOEXEC)失败");
close(inotify_fd_nonblock);
close(inotify_fd_cloexec);
exit(EXIT_FAILURE);
}
show_inotify_info(inotify_fd_both, "非阻塞+close-on-exec模式");
// 创建测试目录
const char* test_dir = "advanced_inotify_test";
if (mkdir(test_dir, 0755) == -1 && errno != EEXIST) {
perror("创建测试目录失败");
close(inotify_fd_nonblock);
close(inotify_fd_cloexec);
close(inotify_fd_both);
exit(EXIT_FAILURE);
}
// 为非阻塞实例添加监控
int watch_desc = inotify_add_watch(inotify_fd_nonblock, test_dir,
IN_CREATE | IN_DELETE | IN_MODIFY);
if (watch_desc == -1) {
perror("添加监控失败");
close(inotify_fd_nonblock);
close(inotify_fd_cloexec);
close(inotify_fd_both);
rmdir(test_dir);
exit(EXIT_FAILURE);
}
printf("为非阻塞实例添加监控: %s\n", test_dir);
// 演示非阻塞读取
printf("\n4. 演示非阻塞读取:\n");
printf("立即读取非阻塞实例...\n");
nonblocking_read_events(inotify_fd_nonblock);
// 创建测试文件
char test_file[256];
snprintf(test_file, sizeof(test_file), "%s/test_file.txt", test_dir);
int fd = open(test_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "测试内容", 8);
close(fd);
printf("创建测试文件: %s\n", test_file);
}
// 再次读取(应该有事件)
sleep(1);
printf("创建文件后读取事件:\n");
nonblocking_read_events(inotify_fd_nonblock);
// 演示poll使用
printf("\n5. 演示poll使用:\n");
// poll等待事件
poll_inotify_events(inotify_fd_nonblock, 2000); // 2秒超时
// 修改文件
fd = open(test_file, O_WRONLY | O_APPEND);
if (fd != -1) {
write(fd, " 追加内容", 9);
close(fd);
printf("修改测试文件\n");
}
// poll等待事件
poll_inotify_events(inotify_fd_nonblock, 2000);
// 删除文件
if (unlink(test_file) == 0) {
printf("删除测试文件\n");
}
// poll等待事件
poll_inotify_events(inotify_fd_nonblock, 2000);
// 演示错误处理
printf("\n6. 错误处理演示:\n");
// 使用无效标志
int invalid_fd = inotify_init1(0xFFFFFFFF);
if (invalid_fd == -1) {
printf("使用无效标志创建inotify失败 (预期行为): %s\n", strerror(errno));
}
// 关闭已关闭的文件描述符
close(inotify_fd_nonblock);
printf("关闭非阻塞实例\n");
int result = inotify_add_watch(inotify_fd_nonblock, test_dir, IN_CREATE);
if (result == -1) {
printf("在已关闭的fd上添加监控失败 (预期行为): %s\n", strerror(errno));
}
// 清理资源
printf("\n7. 清理资源:\n");
close(inotify_fd_cloexec);
close(inotify_fd_both);
rmdir(test_dir);
printf("清理完成\n");
printf("\n=== inotify_init1高级选项演示完成 ===\n");
return 0;
}
示例3:多目录监控和事件处理 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include <sys/stat.h>
#define MAX_WATCHES 64
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
// 监控目标信息
typedef struct {
int wd; // 监控描述符
char path[256]; // 监控路径
int event_mask; // 监控事件掩码
time_t last_event_time; // 最后事件时间
int event_count; // 事件计数
} watch_info_t;
watch_info_t watches[MAX_WATCHES];
int watch_count = 0;
int inotify_fd = -1;
// 添加监控目标
int add_watch_target(const char* path, int event_mask) {
if (watch_count >= MAX_WATCHES) {
fprintf(stderr, "监控目标数量已达上限\n");
return -1;
}
int wd = inotify_add_watch(inotify_fd, path, event_mask);
if (wd == -1) {
perror("添加监控失败");
return -1;
}
watches[watch_count].wd = wd;
strncpy(watches[watch_count].path, path, sizeof(watches[watch_count].path) - 1);
watches[watch_count].event_mask = event_mask;
watches[watch_count].last_event_time = 0;
watches[watch_count].event_count = 0;
printf("添加监控: %s (wd=%d)\n", path, wd);
watch_count++;
return wd;
}
// 根据监控描述符查找监控信息
watch_info_t* find_watch_info(int wd) {
for (int i = 0; i < watch_count; i++) {
if (watches[i].wd == wd) {
return &watches[i];
}
}
return NULL;
}
// 解析事件掩码为可读字符串
void parse_event_mask(uint32_t mask, char* buffer, size_t buffer_size) {
buffer[0] = '\0';
if (mask & IN_ACCESS) strcat(buffer, "访问 ");
if (mask & IN_MODIFY) strcat(buffer, "修改 ");
if (mask & IN_ATTRIB) strcat(buffer, "属性改变 ");
if (mask & IN_CLOSE_WRITE) strcat(buffer, "写入关闭 ");
if (mask & IN_CLOSE_NOWRITE) strcat(buffer, "只读关闭 ");
if (mask & IN_OPEN) strcat(buffer, "打开 ");
if (mask & IN_MOVED_FROM) strcat(buffer, "移出 ");
if (mask & IN_MOVED_TO) strcat(buffer, "移入 ");
if (mask & IN_CREATE) strcat(buffer, "创建 ");
if (mask & IN_DELETE) strcat(buffer, "删除 ");
if (mask & IN_DELETE_SELF) strcat(buffer, "自我删除 ");
if (mask & IN_MOVE_SELF) strcat(buffer, "自我移动 ");
// 移除末尾空格
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == ' ') {
buffer[len - 1] = '\0';
}
}
// 处理inotify事件
void handle_inotify_event(struct inotify_event* event) {
time_t now = time(NULL);
char time_str[64];
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", localtime(&now));
watch_info_t* watch_info = find_watch_info(event->wd);
const char* watch_path = watch_info ? watch_info->path : "未知路径";
printf("[%s] 监控路径: %s\n", time_str, watch_path);
if (event->len > 0) {
printf(" 文件: %s\n", event->name);
}
char event_desc[256];
parse_event_mask(event->mask, event_desc, sizeof(event_desc));
printf(" 事件: %s\n", event_desc);
if (event->cookie > 0) {
printf(" Cookie: %u (用于关联移动事件)\n", event->cookie);
}
// 更新监控信息
if (watch_info) {
watch_info->last_event_time = now;
watch_info->event_count++;
}
printf("\n");
}
// 显示监控统计信息
void show_monitoring_statistics() {
printf("=== 监控统计信息 ===\n");
printf("%-30s %-10s %-20s %-10s\n", "路径", "描述符", "最后事件时间", "事件计数");
printf("%-30s %-10s %-20s %-10s\n", "----", "------", "----------", "--------");
for (int i = 0; i < watch_count; i++) {
char time_str[32] = "从未";
if (watches[i].last_event_time > 0) {
strftime(time_str, sizeof(time_str), "%H:%M:%S",
localtime(&watches[i].last_event_time));
}
printf("%-30s %-10d %-20s %-10d\n",
watches[i].path,
watches[i].wd,
time_str,
watches[i].event_count);
}
printf("==================\n\n");
}
// 创建复杂的测试环境
int create_complex_test_environment() {
printf("创建复杂测试环境...\n");
// 创建主目录结构
const char* dirs[] = {
"multi_monitor_test",
"multi_monitor_test/documents",
"multi_monitor_test/images",
"multi_monitor_test/videos",
"multi_monitor_test/temp"
};
for (int i = 0; i < 5; i++) {
if (mkdir(dirs[i], 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
return -1;
}
printf("创建目录: %s\n", dirs[i]);
}
// 在不同目录创建测试文件
struct {
const char* dir;
const char* filename;
const char* content;
} files[] = {
{"multi_monitor_test", "readme.txt", "主目录文件"},
{"multi_monitor_test/documents", "doc1.pdf", "文档1"},
{"multi_monitor_test/documents", "doc2.pdf", "文档2"},
{"multi_monitor_test/images", "photo1.jpg", "图片1"},
{"multi_monitor_test/images", "photo2.png", "图片2"},
{"multi_monitor_test/videos", "video1.mp4", "视频1"}
};
for (int i = 0; i < 6; i++) {
char filepath[512];
snprintf(filepath, sizeof(filepath), "%s/%s", files[i].dir, files[i].filename);
int fd = open(filepath, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, files[i].content, strlen(files[i].content));
close(fd);
printf("创建文件: %s\n", filepath);
}
}
return 0;
}
// 监控循环
int monitoring_loop(int timeout_seconds) {
printf("开始监控循环 (超时: %d 秒)...\n", timeout_seconds);
printf("在另一个终端中操作文件来测试监控功能\n");
printf("例如: touch multi_monitor_test/newfile.txt\n");
printf(" rm multi_monitor_test/documents/doc1.pdf\n");
printf(" echo 'test' >> multi_monitor_test/readme.txt\n\n");
time_t start_time = time(NULL);
int total_events = 0;
while (time(NULL) - start_time < timeout_seconds) {
struct pollfd pfd = {
.fd = inotify_fd,
.events = POLLIN,
.revents = 0
};
// 等待事件(1秒超时)
int ret = poll(&pfd, 1, 1000);
if (ret == -1) {
if (errno == EINTR) {
continue; // 被信号中断
}
perror("poll失败");
break;
} else if (ret == 0) {
// 超时,显示统计信息
if ((time(NULL) - start_time) % 10 == 0) { // 每10秒显示一次
show_monitoring_statistics();
}
continue;
}
// 读取事件
char buffer[BUF_LEN];
ssize_t bytes_read = read(inotify_fd, buffer, BUF_LEN);
if (bytes_read > 0) {
for (char* ptr = buffer; ptr < buffer + bytes_read; ) {
struct inotify_event* event = (struct inotify_event*)ptr;
handle_inotify_event(event);
total_events++;
ptr += EVENT_SIZE + event->len;
}
} else if (bytes_read == -1) {
if (errno != EAGAIN) {
perror("读取事件失败");
}
}
}
printf("监控循环结束,总共处理 %d 个事件\n", total_events);
return total_events;
}
int main() {
printf("=== 多目录监控和事件处理示例 ===\n");
// 初始化inotify
inotify_fd = inotify_init1(IN_NONBLOCK);
if (inotify_fd == -1) {
perror("inotify_init1失败");
exit(EXIT_FAILURE);
}
printf("inotify文件描述符: %d\n", inotify_fd);
// 创建测试环境
if (create_complex_test_environment() == -1) {
close(inotify_fd);
exit(EXIT_FAILURE);
}
// 添加多个监控目标
printf("\n添加监控目标:\n");
// 监控主目录(所有事件)
add_watch_target("multi_monitor_test",
IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM |
IN_MOVED_TO | IN_ACCESS | IN_ATTRIB);
// 监控文档目录(创建和删除)
add_watch_target("multi_monitor_test/documents",
IN_CREATE | IN_DELETE);
// 监控图片目录(修改和访问)
add_watch_target("multi_monitor_test/images",
IN_MODIFY | IN_ACCESS);
// 监控临时目录(所有事件,包括子目录)
add_watch_target("multi_monitor_test/temp",
IN_ALL_EVENTS);
show_monitoring_statistics();
// 运行监控循环
printf("运行监控循环 60 秒...\n");
int total_events = monitoring_loop(60);
// 显示最终统计
printf("\n最终统计信息:\n");
show_monitoring_statistics();
printf("总计处理事件: %d\n", total_events);
// 清理资源
printf("清理资源:\n");
for (int i = 0; i < watch_count; i++) {
if (inotify_rm_watch(inotify_fd, watches[i].wd) == 0) {
printf("移除监控: %s (wd=%d)\n", watches[i].path, watches[i].wd);
}
}
close(inotify_fd);
printf("关闭inotify实例\n");
// 清理测试文件
printf("清理测试文件:\n");
const char* files_to_delete[] = {
"multi_monitor_test/readme.txt",
"multi_monitor_test/documents/doc1.pdf",
"multi_monitor_test/documents/doc2.pdf",
"multi_monitor_test/images/photo1.jpg",
"multi_monitor_test/images/photo2.png",
"multi_monitor_test/videos/video1.mp4"
};
for (int i = 0; i < 6; i++) {
if (unlink(files_to_delete[i]) == 0) {
printf("删除文件: %s\n", files_to_delete[i]);
}
}
const char* dirs_to_delete[] = {
"multi_monitor_test/documents",
"multi_monitor_test/images",
"multi_monitor_test/videos",
"multi_monitor_test/temp",
"multi_monitor_test"
};
for (int i = 0; i < 5; i++) {
if (rmdir(dirs_to_delete[i]) == 0) {
printf("删除目录: %s\n", dirs_to_delete[i]);
}
}
printf("\n=== 多目录监控演示完成 ===\n");
return 0;
}
示例4:实时文件同步应用 見出しへのリンク
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <poll.h>
#include <time.h>
#include <sys/stat.h>
#include <libgen.h>
#define MAX_WATCHES 128
#define EVENT_SIZE (sizeof(struct inotify_event))
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
// 同步任务结构
typedef struct {
int wd;
char source_path[512];
char target_path[512];
int recursive;
int sync_on_create;
int sync_on_modify;
int sync_on_delete;
} sync_task_t;
sync_task_t sync_tasks[MAX_WATCHES];
int task_count = 0;
int inotify_fd = -1;
// 文件同步操作类型
typedef enum {
SYNC_CREATE,
SYNC_MODIFY,
SYNC_DELETE,
SYNC_MOVE
} sync_operation_t;
// 添加同步任务
int add_sync_task(const char* source, const char* target, int recursive,
int create, int modify, int delete) {
if (task_count >= MAX_WATCHES) {
fprintf(stderr, "同步任务数量已达上限\n");
return -1;
}
// 创建目标目录
struct stat target_stat;
if (stat(target, &target_stat) == -1) {
if (mkdir(target, 0755) == -1) {
perror("创建目标目录失败");
return -1;
}
printf("创建目标目录: %s\n", target);
}
// 添加inotify监控
int event_mask = 0;
if (create) event_mask |= IN_CREATE;
if (modify) event_mask |= IN_MODIFY;
if (delete) event_mask |= IN_DELETE;
if (recursive) event_mask |= IN_CREATE; // 监控新创建的子目录
int wd = inotify_add_watch(inotify_fd, source, event_mask);
if (wd == -1) {
perror("添加监控失败");
return -1;
}
sync_tasks[task_count].wd = wd;
strncpy(sync_tasks[task_count].source_path, source,
sizeof(sync_tasks[task_count].source_path) - 1);
strncpy(sync_tasks[task_count].target_path, target,
sizeof(sync_tasks[task_count].target_path) - 1);
sync_tasks[task_count].recursive = recursive;
sync_tasks[task_count].sync_on_create = create;
sync_tasks[task_count].sync_on_modify = modify;
sync_tasks[task_count].sync_on_delete = delete;
printf("添加同步任务:\n");
printf(" 源路径: %s\n", source);
printf(" 目标路径: %s\n", target);
printf(" 递归: %s\n", recursive ? "是" : "否");
printf(" 监控描述符: %d\n", wd);
task_count++;
return wd;
}
// 构造目标文件路径
void construct_target_path(const char* source_root, const char* target_root,
const char* relative_path, char* target_path, size_t path_size) {
if (relative_path && strlen(relative_path) > 0) {
snprintf(target_path, path_size, "%s/%s", target_root, relative_path);
} else {
snprintf(target_path, path_size, "%s", target_root);
}
}
// 复制文件
int copy_file(const char* source, const char* target) {
printf("复制文件: %s -> %s\n", source, target);
// 确保目标目录存在
char target_dir[512];
strncpy(target_dir, target, sizeof(target_dir) - 1);
char* dir_part = dirname(target_dir);
if (strcmp(dir_part, ".") != 0 && strcmp(dir_part, "/") != 0) {
// 创建目录(简化实现,实际应该递归创建)
struct stat dir_stat;
if (stat(dir_part, &dir_stat) == -1) {
mkdir(dir_part, 0755);
}
}
int src_fd = open(source, O_RDONLY);
if (src_fd == -1) {
printf("无法打开源文件: %s\n", strerror(errno));
return -1;
}
int dst_fd = open(target, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (dst_fd == -1) {
printf("无法创建目标文件: %s\n", strerror(errno));
close(src_fd);
return -1;
}
char buffer[8192];
ssize_t bytes_read, bytes_written;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
printf("写入目标文件失败\n");
close(src_fd);
close(dst_fd);
return -1;
}
}
close(src_fd);
close(dst_fd);
printf("文件复制完成\n");
return 0;
}
// 删除文件
int delete_file(const char* filepath) {
printf("删除文件: %s\n", filepath);
if (unlink(filepath) == 0) {
printf("文件删除成功\n");
return 0;
} else {
printf("文件删除失败: %s\n", strerror(errno));
return -1;
}
}
// 查找同步任务
sync_task_t* find_sync_task(int wd) {
for (int i = 0; i < task_count; i++) {
if (sync_tasks[i].wd == wd) {
return &sync_tasks[i];
}
}
return NULL;
}
// 处理文件系统事件并执行同步
void handle_sync_event(struct inotify_event* event) {
sync_task_t* task = find_sync_task(event->wd);
if (!task) {
return;
}
time_t now = time(NULL);
char time_str[32];
strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&now));
printf("[%s] 处理同步事件:\n", time_str);
printf(" 源目录: %s\n", task->source_path);
printf(" 目标目录: %s\n", task->target_path);
if (event->len > 0) {
printf(" 文件: %s\n", event->name);
}
// 构造完整路径
char source_full_path[1024];
char target_full_path[1024];
if (event->len > 0) {
snprintf(source_full_path, sizeof(source_full_path), "%s/%s",
task->source_path, event->name);
snprintf(target_full_path, sizeof(target_full_path), "%s/%s",
task->target_path, event->name);
} else {
strncpy(source_full_path, task->source_path, sizeof(source_full_path) - 1);
strncpy(target_full_path, task->target_path, sizeof(target_full_path) - 1);
}
// 根据事件类型执行同步操作
if ((event->mask & IN_CREATE) && task->sync_on_create) {
struct stat file_stat;
if (stat(source_full_path, &file_stat) == 0) {
if (S_ISREG(file_stat.st_mode)) {
copy_file(source_full_path, target_full_path);
} else if (S_ISDIR(file_stat.st_mode) && task->recursive) {
printf("创建目录: %s\n", target_full_path);
mkdir(target_full_path, 0755);
}
}
} else if ((event->mask & IN_MODIFY) && task->sync_on_modify) {
struct stat file_stat;
if (stat(source_full_path, &file_stat) == 0 && S_ISREG(file_stat.st_mode)) {
copy_file(source_full_path, target_full_path);
}
} else if ((event->mask & IN_DELETE) && task->sync_on_delete) {
delete_file(target_full_path);
}
printf("\n");
}
// 创建同步测试环境
int create_sync_test_environment() {
printf("创建同步测试环境...\n");
// 创建源目录
if (mkdir("sync_source", 0755) == -1 && errno != EEXIST) {
perror("创建源目录失败");
return -1;
}
// 创建子目录
if (mkdir("sync_source/subdir1", 0755) == -1 && errno != EEXIST) {
perror("创建子目录失败");
return -1;
}
if (mkdir("sync_source/subdir2", 0755) == -1 && errno != EEXIST) {
perror("创建子目录失败");
return -1;
}
// 创建测试文件
struct {
const char* path;
const char* content;
} test_files[] = {
{"sync_source/file1.txt", "文件1内容"},
{"sync_source/file2.txt", "文件2内容"},
{"sync_source/subdir1/subfile1.txt", "子目录1文件1"},
{"sync_source/subdir1/subfile2.txt", "子目录1文件2"},
{"sync_source/subdir2/subfile1.txt", "子目录2文件1"}
};
for (int i = 0; i < 5; i++) {
int fd = open(test_files[i].path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, test_files[i].content, strlen(test_files[i].content));
close(fd);
printf("创建文件: %s\n", test_files[i].path);
}
}
return 0;
}
// 显示同步状态
void show_sync_status() {
printf("\n=== 同步任务状态 ===\n");
printf("%-20s %-20s %-8s %-8s %-8s %-8s\n",
"源路径", "目标路径", "递归", "创建", "修改", "删除");
printf("%-20s %-20s %-8s %-8s %-8s %-8s\n",
"--------", "--------", "----", "----", "----", "----");
for (int i = 0; i < task_count; i++) {
printf("%-20s %-20s %-8s %-8s %-8s %-8s\n",
sync_tasks[i].source_path,
sync_tasks[i].target_path,
sync_tasks[i].recursive ? "是" : "否",
sync_tasks[i].sync_on_create ? "是" : "否",
sync_tasks[i].sync_on_modify ? "是" : "否",
sync_tasks[i].sync_on_delete ? "是" : "否");
}
printf("==================\n\n");
}
// 同步监控循环
int sync_monitoring_loop(int duration_seconds) {
printf("开始同步监控 %d 秒...\n", duration_seconds);
printf("请在另一个终端操作源目录来测试同步功能\n");
printf("例如: touch sync_source/newfile.txt\n");
printf(" echo 'test' >> sync_source/file1.txt\n");
printf(" rm sync_source/file2.txt\n\n");
time_t start_time = time(NULL);
int sync_events = 0;
while (time(NULL) - start_time < duration_seconds) {
struct pollfd pfd = {
.fd = inotify_fd,
.events = POLLIN,
.revents = 0
};
// 等待事件(1秒超时)
int ret = poll(&pfd, 1, 1000);
if (ret == -1) {
if (errno == EINTR) {
continue;
}
perror("poll失败");
break;
} else if (ret == 0) {
continue;
}
// 读取事件
char buffer[BUF_LEN];
ssize_t bytes_read = read(inotify_fd, buffer, BUF_LEN);
if (bytes_read > 0) {
for (char* ptr = buffer; ptr < buffer + bytes_read; ) {
struct inotify_event* event = (struct inotify_event*)ptr;
handle_sync_event(event);
sync_events++;
ptr += EVENT_SIZE + event->len;
}
}
}
printf("同步监控结束,处理了 %d 个同步事件\n", sync_events);
return sync_events;
}
int main() {
printf("=== 实时文件同步应用示例 ===\n");
// 初始化inotify
inotify_fd = inotify_init1(IN_NONBLOCK);
if (inotify_fd == -1) {
perror("inotify_init1失败");
exit(EXIT_FAILURE);
}
printf("inotify文件描述符: %d\n", inotify_fd);
// 创建测试环境
if (create_sync_test_environment() == -1) {
close(inotify_fd);
exit(EXIT_FAILURE);
}
// 创建目标目录
if (mkdir("sync_target", 0755) == -1 && errno != EEXIST) {
perror("创建目标目录失败");
close(inotify_fd);
exit(EXIT_FAILURE);
}
// 添加同步任务
printf("\n添加同步任务:\n");
add_sync_task("sync_source", "sync_target", 1, 1, 1, 1);
show_sync_status();
// 运行同步监控
printf("运行同步监控 120 秒...\n");
int total_sync_events = sync_monitoring_loop(120);
// 显示最终同步结果
printf("\n最终同步结果:\n");
show_sync_status();
printf("总计同步事件: %d\n", total_sync_events);
// 验证同步结果
printf("\n验证同步结果:\n");
const char* synced_files[] = {
"sync_target/file1.txt",
"sync_target/file2.txt",
"sync_target/subdir1/subfile1.txt",
"sync_target/subdir1/subfile2.txt",
"sync_target/subdir2/subfile1.txt"
};
for (int i = 0; i < 5; i++) {
if (access(synced_files[i], F_OK) == 0) {
printf("✓ 文件已同步: %s\n", synced_files[i]);
} else {
printf("✗ 文件未同步: %s\n", synced_files[i]);
}
}
// 清理资源
printf("\n清理资源:\n");
for (int i = 0; i < task_count; i++) {
if (inotify_rm_watch(inotify_fd, sync_tasks[i].wd) == 0) {
printf("移除监控: %s\n", sync_tasks[i].source_path);
}
}
close(inotify_fd);
printf("关闭inotify实例\n");
// 清理测试文件
printf("清理测试文件:\n");
const char* files_to_delete[] = {
"sync_source/file1.txt",
"sync_source/file2.txt",
"sync_source/subdir1/subfile1.txt",
"sync_source/subdir1/subfile2.txt",
"sync_source/subdir2/subfile1.txt",
"sync_target/file1.txt",
"sync_target/file2.txt",
"sync_target/subdir1/subfile1.txt",
"sync_target/subdir1/subfile2.txt",
"sync_target/subdir2/subfile1.txt"
};
for (int i = 0; i < 10; i++) {
unlink(files_to_delete[i]);
}
rmdir("sync_source/subdir1");
rmdir("sync_source/subdir2");
rmdir("sync_source");
rmdir("sync_target/subdir1");
rmdir("sync_target/subdir2");
rmdir("sync_target");
printf("清理完成\n");
printf("\n=== 实时文件同步演示完成 ===\n");
return 0;
}
编译和运行 見出しへのリンク
# 编译示例1
gcc -o inotify_example1 inotify_example1.c
./inotify_example1
# 编译示例2
gcc -o inotify_example2 inotify_example2.c
./inotify_example2
# 编译示例3
gcc -o inotify_example3 inotify_example3.c
./inotify_example3
# 编译示例4
gcc -o inotify_example4 inotify_example4.c
./inotify_example4
重要注意事项 見出しへのリンク
- 文件描述符限制: 系统和进程都有文件描述符数量限制
- 事件队列: inotify有事件队列大小限制,过多事件可能导致丢失
- 符号链接: inotify监控符号链接的目标,不是链接本身
- 子目录: 默认不递归监控子目录,需要显式添加
- 性能影响: 大量监控可能影响系统性能
- 权限要求: 需要对监控目录有适当的读取权限
- 资源管理: 必须正确关闭inotify实例和移除监控
最佳实践 見出しへのリンク
- 合理使用标志: 根据需要选择合适的inotify_init1标志
- 事件过滤: 只监控需要的事件类型以提高性能
- 错误处理: 完善的错误处理和恢复机制
- 资源清理: 及时清理不需要的监控和关闭文件描述符
- 批量操作: 使用poll或select批量处理事件
- 日志记录: 记录重要的监控事件用于调试和审计
- 性能监控: 监控inotify对系统性能的影响
通过这些示例,你可以理解inotify在文件系统监控方面的强大功能,它为Linux系统提供了高效、实时的文件变化检测能力,广泛应用于文件同步、系统监控、开发工具等场景。