inotify_init 和 inotify_init1 函数详解 Link to heading

1. 函数介绍 Link to heading

inotify_init和inotify_init1函数是Linux系统中用于文件系统事件监控的函数。可以把它们想象成"文件系统监控摄像头",能够实时监控文件和目录的各种变化,如创建、删除、修改、访问等事件。

inotify是Linux 2.6.13内核引入的文件系统事件监控机制,它比传统的轮询方式更加高效和实时。当文件系统发生变化时,内核会立即通知监控程序,而不需要程序主动去检查文件状态。

inotify_init是基础版本,创建一个inotify实例并返回文件描述符。 inotify_init1是增强版本,支持额外的标志参数来控制inotify实例的行为。

使用场景:

  • 文件同步工具(如rsync、syncthing)
  • 实时日志监控
  • 文件系统审计
  • 开发工具的自动重新加载
  • 系统监控和入侵检测
  • 备份软件的增量备份

2. 函数原型 Link to heading

#include <sys/inotify.h>

// 基础版本
int inotify_init(void);

// 增强版本
int inotify_init1(int flags);

3. 功能 Link to heading

这两个函数的主要功能是创建一个inotify实例,用于监控文件系统事件:

  • inotify_init: 创建标准的inotify实例
  • inotify_init1: 创建支持额外选项的inotify实例

4. 参数 Link to heading

inotify_init1参数: Link to heading

  • flags: 控制标志
    • 类型:int
    • 含义:控制inotify实例行为的标志位
    • 常用值:
      • 0:默认行为(等同于inotify_init)
      • IN_CLOEXEC:设置close-on-exec标志
      • IN_NONBLOCK:设置非阻塞I/O模式

5. 返回值 Link to heading

  • 成功: 返回inotify实例的文件描述符(非负整数)
  • 失败: 返回-1,并设置errno错误码
    • EMFILE:进程文件描述符达到限制
    • ENFILE:系统文件描述符达到限制
    • ENOMEM:内存不足
    • EINVAL:flags参数无效(inotify_init1)

6. 相似函数或关联函数 Link to heading

  • inotify_add_watch(): 添加监控目标
  • inotify_rm_watch(): 移除监控目标
  • read(): 读取事件数据
  • close(): 关闭inotify实例
  • poll(): 监控inotify文件描述符
  • select(): 监控inotify文件描述符
  • fanotify: 更高级的文件系统监控接口

7. 示例代码 Link to heading

示例1:基础inotify使用 - 简单文件监控 Link to heading

#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使用 - 高级监控选项 Link to heading

#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:多目录监控和事件处理 Link to heading

#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:实时文件同步应用 Link to heading

#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;
}

编译和运行 Link to heading

# 编译示例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

重要注意事项 Link to heading

  1. 文件描述符限制: 系统和进程都有文件描述符数量限制
  2. 事件队列: inotify有事件队列大小限制,过多事件可能导致丢失
  3. 符号链接: inotify监控符号链接的目标,不是链接本身
  4. 子目录: 默认不递归监控子目录,需要显式添加
  5. 性能影响: 大量监控可能影响系统性能
  6. 权限要求: 需要对监控目录有适当的读取权限
  7. 资源管理: 必须正确关闭inotify实例和移除监控

最佳实践 Link to heading

  1. 合理使用标志: 根据需要选择合适的inotify_init1标志
  2. 事件过滤: 只监控需要的事件类型以提高性能
  3. 错误处理: 完善的错误处理和恢复机制
  4. 资源清理: 及时清理不需要的监控和关闭文件描述符
  5. 批量操作: 使用poll或select批量处理事件
  6. 日志记录: 记录重要的监控事件用于调试和审计
  7. 性能监控: 监控inotify对系统性能的影响

通过这些示例,你可以理解inotify在文件系统监控方面的强大功能,它为Linux系统提供了高效、实时的文件变化检测能力,广泛应用于文件同步、系统监控、开发工具等场景。