【产品化】Linux文件实时监控之inotify

785 阅读6分钟

inotify 是 Linux 内核提供的一种文件系统事件监控机制,允许应用程序监控文件系统中的变化,如文件的创建、删除、修改等。它的主要应用场景包括:

一、应用场景

  1. 文件同步: 在文件同步工具中,inotify 可以用来实时监控文件变化,并及时同步到其他位置或设备。
  2. 日志监控: 对于日志文件的监控,inotify 可以帮助应用程序在日志文件更新时立即读取新内容,而不需要定期轮询。
  3. 实时备份: 在备份工具中,可以使用 inotify 来监控文件变化,并在文件被修改时自动备份。
  4. 配置文件监控: 对于需要动态加载配置的应用程序,可以使用 inotify 监控配置文件的变化,并在文件更新时重新加载配置。
  5. 文件上传监控: 在文件上传服务中,可以使用 inotify 来监控上传目录的变化,并在文件上传后进行处理。
  6. 开发工具: 在开发环境中,inotify 可以用于监控源代码文件的变化,自动触发编译或测试。

二、产品化踩坑点

2.1 兼容性

  1. Linux 内核版本兼容性:
  • 最低内核版本要求: inotify 最初是在 Linux 内核 2.6.13 版本中引入的。这意味着,如果你的 Linux 系统内核版本低于 2.6.13,你将无法使用 inotify。 后续内核版本: 在后续的 Linux 内核版本中,inotify 的功能和性能得到了不断的改进和优化。因此,通常来说,较新的内核版本对 inotify 的支持更好,功能更完善。 内核模块: inotify 是作为内核模块实现的,因此,确保你的内核加载了 inotify 模块。通常情况下,现代 Linux 发行版都会默认加载该模块。你可以使用 lsmod | grep inotify 命令来检查是否加载了该模块。
  1. Linux 发行版兼容性主流发行版: 大多数主流的 Linux 发行版(如 Ubuntu、Debian、Fedora、CentOS 等)都支持 inotify。 特殊发行版: 一些嵌入式系统或特殊用途的 Linux 发行版可能没有默认启用 inotify,或者可能需要手动配置才能使用。 发行版差异: 不同发行版可能在 inotify 的配置和使用上存在一些细微的差异,例如,某些发行版可能对 inotify 的监控项数量有限制。
  2. 文件系统兼容性支持的文件系统: inotify 主要用于监控本地文件系统,例如 ext4、ext3、xfs、btrfs 等。 网络文件系统: 对于网络文件系统(如 NFS、SMB/CIFS),inotify 的支持可能存在一些限制。 NFS: NFS 对 inotify 的支持取决于 NFS 服务器和客户端的配置。某些 NFS 版本可能不支持 inotify,或者可能只支持部分事件类型。 SMB/CIFS: SMB/CIFS 通常不支持 inotify,因为 SMB/CIFS 是基于网络协议的,而不是本地文件系统。 特殊文件系统: 一些特殊的文件系统(如 FUSE 文件系统)可能对 inotify 的支持存在差异。

2.2 系统限制

  1. 监控项数量限制: 系统限制: Linux 系统对 inotify 的监控项数量有限制,可以通过 /proc/sys/fs/inotify/max_user_watches 文件查看。 用户限制: 每个用户可以创建的 inotify 监控项数量也有限制,可以通过 /proc/sys/fs/inotify/max_user_instances 文件查看。 超出限制: 如果超出限制,inotify_add_watch() 函数会返回错误,并且 errno 会被设置为 ENOSPC。 调整限制: 可以通过修改 /etc/sysctl.conf 文件来调整这些限制,例如: fs.inotify.max_user_watches = 524288 fs.inotify.max_user_instances = 1024 然后使用 sysctl -p 命令使配置生效。
  2. 事件队列溢出: 事件队列: inotify 使用一个事件队列来存储发生的事件。 队列溢出: 如果事件发生的速度过快,或者应用程序没有及时读取事件,事件队列可能会溢出,导致部分事件丢失。 IN_Q_OVERFLOW 事件: 当事件队列溢出时,inotify 会产生一个 IN_Q_OVERFLOW 事件,应用程序可以通过该事件来检测队列溢出。 处理方法: 为了避免事件队列溢出,应用程序应该及时读取事件,或者增加事件队列的大小。

三、用法

linux官网API介绍:[www.man7.org/linux/man-p…]

3.1 使用 inotify 主要涉及以下几个步骤:

  1. 创建 inotify 实例: 使用 inotify_init() 或 inotify_init1() 创建一个 inotify 实例。
int fd = inotify_init();
  1. 添加监控: 使用 inotify_add_watch() 添加需要监控的文件或目录,并指定要监控的事件类型。
int wd = inotify_add_watch(fd, "/path/to/directory", IN_MODIFY | IN_CREATE | IN_DELETE);
  • 使用 inotify_add_watch() 函数添加要监控的文件或目录,并指定要监控的事件类型。 可以监控的事件类型包括: IN_ACCESS: 文件被访问 IN_MODIFY: 文件被修改 IN_ATTRIB: 文件属性被修改 IN_CLOSE_WRITE: 文件被写入后关闭 IN_CLOSE_NOWRITE: 文件被非写入方式关闭 IN_OPEN: 文件被打开 IN_MOVED_FROM: 文件被移动(源) IN_MOVED_TO: 文件被移动(目标) IN_CREATE: 文件或目录被创建 IN_DELETE: 文件或目录被删除 IN_DELETE_SELF: 监控项本身被删除 IN_MOVE_SELF: 监控项本身被移动 IN_UNMOUNT: 文件系统被卸载 IN_Q_OVERFLOW: 事件队列溢出 IN_IGNORED: 监控项被忽略 IN_ISDIR: 事件发生在目录上 IN_ONESHOT: 只触发一次事件 IN_ALL_EVENTS: 监控所有事件
  1. 读取事件: 使用 read() 函数读取事件,事件会被存储在一个缓冲区中。
char buffer[1024];
int length = read(fd, buffer, sizeof(buffer));
  1. 处理事件: 解析读取到的事件,处理相应的逻辑。
struct inotify_event *event = (struct inotify_event *) &buffer[i];
if (event->mask & IN_CREATE) {
    printf("File %s created.\n", event->name);
}
  1. 清理: 完成监控后,使用 inotify_rm_watch() 移除监控,并使用 close() 关闭 inotify 文件描述符。
inotify_rm_watch(fd, wd);
close(fd);

3.2 示例代码

以下是一个简单的示例,监控指定目录的文件创建和修改事件:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <limits.h>

int main(int argc,char* argv[])
{
    // 创建inotify实例
    int fd = inotify_init();
    if(fd < 0){
        perror("inotify_init failed.");
        exit(EXIT_FAILURE);
    }

    // 添加监控
    int wd = inotify_add_watch(fd,"/tmp/test",IN_CREATE|IN_DELETE|IN_MODIFY);
    if (wd == -1) {
        perror("inotify_add_watch");
        exit(EXIT_FAILURE);
    }

    do
    {
        // 读取事件
        char buffer[1024] = { 0 };
        int len = read(fd,buffer,sizeof(buffer));
        if (len < 0) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        for (int i = 0; i < len; ) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];
            if (event->mask & IN_CREATE) {
                printf("File %s created.\n", event->name);
            }
            else if (event->mask & IN_MODIFY) {
                printf("File %s modified.\n", event->name);
            }
            else if (event->mask & IN_DELETE) {
                printf("File %s deleted.\n", event->name);
            }
            i += sizeof(struct inotify_event) + event->len;
        }

    } while (true);
    
    // 移除监控
    int rd = inotify_rm_watch(fd,wd);
    
    // 关闭实例
    int ret = close(fd);

    return 0;
}

3.3 测试结果

测试用例执行结果

3.4 注意事项

inotify 适用于监控本地文件系统的变化,不支持网络文件系统(如 NFS)。 监控的文件数量和事件数量是有限制的,可以通过调整 /proc/sys/fs/inotify/max_user_watches 来增加监控数量。 事件的处理需要尽量高效,以避免阻塞 read() 调用。