C#在Linux上如何使用Inotify高效监控文件

来源:图像处理网作者:广州网站建设头衔:草根站长
导读:本期聚焦于小伙伴创作的《C#在Linux上如何使用Inotify高效监控文件》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#在Linux上如何使用Inotify高效监控文件》有用,将其分享出去将是对创作者最好的鼓励。

在Linux系统中,Inotify是内核提供的文件系统事件监控机制,能够实时捕获文件或目录的创建、修改、删除、移动等变更事件,相比轮询式文件监控,它的资源占用更低、响应速度更快。C#开发者在Linux环境下开发文件监控功能时,可以通过调用系统接口使用Inotify实现高效的文件变更感知。

C#在Linux上如何使用Inotify高效监控文件

Inotify核心概念

Inotify的核心能力由几个关键部分构成,理解这些概念能帮助我们更好地实现监控逻辑:

  • inotify实例:通过系统调用创建的一个监控实例,后续所有监控操作都基于这个实例完成。
  • 监控描述符:向inotify实例添加需要监控的文件或目录后,会返回一个唯一的监控描述符,用于标识该监控项。
  • 事件类型:Inotify支持多种事件,比如IN_CREATE(文件创建)、IN_MODIFY(文件修改)、IN_DELETE(文件删除)、IN_MOVED_FROM(文件移出)、IN_MOVED_TO(文件移入)等。

C#调用Inotify的实现步骤

1. 初始化Inotify实例

在Linux中,创建inotify实例需要调用inotify_init系统调用,C#可以通过Interop的方式调用该系统调用,首先定义相关的系统调用方法:

using System;
using System.Runtime.InteropServices;

public class InotifyHelper
{
    // 系统调用编号,不同Linux架构可能略有差异,x86_64下inotify_init的系统调用号为253
    private const int SYS_INOTIFY_INIT = 253;
    private const int SYS_INOTIFY_ADD_WATCH = 254;
    private const int SYS_INOTIFY_RM_WATCH = 255;

    [DllImport("libc", SetLastError = true)]
    private static extern int syscall(int number, params object[] args);

    // 创建inotify实例,返回实例的文件描述符
    public static int InitInotify()
    {
        int fd = syscall(SYS_INOTIFY_INIT);
        if (fd < 0)
        {
            throw new Exception($"初始化Inotify失败,错误码:{Marshal.GetLastWin32Error()}");
        }
        return fd;
    }
}

2. 添加监控目录或文件

创建好inotify实例后,需要调用inotify_add_watch系统调用添加需要监控的路径,同时指定要监听的事件类型:

public class InotifyHelper
{
    // 常用事件掩码定义
    public const uint IN_CREATE = 0x00000100;
    public const uint IN_MODIFY = 0x00000002;
    public const uint IN_DELETE = 0x00000200;
    public const uint IN_MOVED_FROM = 0x00000040;
    public const uint IN_MOVED_TO = 0x00000080;
    public const uint IN_ALL_EVENTS = 0xFFFFFFFF;

    // 添加监控,返回监控描述符
    public static int AddWatch(int inotifyFd, string path, uint mask)
    {
        int wd = syscall(SYS_INOTIFY_ADD_WATCH, inotifyFd, path, mask);
        if (wd < 0)
        {
            throw new Exception($"添加监控路径{path}失败,错误码:{Marshal.GetLastWin32Error()}");
        }
        return wd;
    }
}

3. 读取并处理监控事件

Inotify的事件会存储在内核缓冲区中,我们可以通过读取inotify实例的文件描述符获取事件数据,事件的结构体定义如下:

[StructLayout(LayoutKind.Sequential)]
public struct InotifyEvent
{
    public int wd;       // 监控描述符
    public uint mask;    // 事件类型掩码
    public uint cookie;  // 关联事件的cookie,比如移动事件的源和目标会共享同一个cookie
    public uint len;     // 事件名称的长度
    // 事件名称紧跟在结构体后面,这里用Marshal读取的时候需要额外处理
}

读取事件的实现代码如下:

using System.IO;
using System.Text;

public class InotifyHelper
{
    // 读取事件,返回事件列表
    public static List<InotifyEventInfo> ReadEvents(int inotifyFd)
    {
        List<InotifyEventInfo> events = new List<InotifyEventInfo>();
        byte[] buffer = new byte[4096];
        // 读取内核缓冲区中的事件数据
        int bytesRead = (int)syscall(0, inotifyFd, buffer, buffer.Length); // 0是read系统调用号
        if (bytesRead <= 0)
        {
            return events;
        }

        int offset = 0;
        while (offset < bytesRead)
        {
            // 读取事件基础结构体
            IntPtr ptr = Marshal.UnsafeAddrOfArrayElement(buffer, offset);
            InotifyEvent inotifyEvent = Marshal.PtrToStructure<InotifyEvent>(ptr);
            offset += Marshal.SizeOf<InotifyEvent>();

            // 读取事件对应的文件名称
            string name = string.Empty;
            if (inotifyEvent.len > 0)
            {
                name = Encoding.UTF8.GetString(buffer, offset, (int)inotifyEvent.len).TrimEnd('');
                offset += (int)inotifyEvent.len;
            }

            events.Add(new InotifyEventInfo
            {
                WatchDescriptor = inotifyEvent.wd,
                EventMask = inotifyEvent.mask,
                Cookie = inotifyEvent.cookie,
                Name = name
            });
        }
        return events;
    }
}

public class InotifyEventInfo
{
    public int WatchDescriptor { get; set; }
    public uint EventMask { get; set; }
    public uint Cookie { get; set; }
    public string Name { get; set; }
}

4. 移除监控与释放资源

当不需要监控某个路径时,可以调用inotify_rm_watch移除监控,程序退出时需要关闭inotify实例的文件描述符释放资源:

public class InotifyHelper
{
    // 移除监控
    public static void RemoveWatch(int inotifyFd, int watchDescriptor)
    {
        int result = syscall(SYS_INOTIFY_RM_WATCH, inotifyFd, watchDescriptor);
        if (result < 0)
        {
            throw new Exception($"移除监控失败,错误码:{Marshal.GetLastWin32Error()}");
        }
    }

    // 关闭inotify实例
    public static void CloseInotify(int inotifyFd)
    {
        syscall(3, inotifyFd); // 3是close系统调用号
    }
}

完整使用示例

下面是一个完整的监控示例,监控指定目录下的文件创建、修改、删除事件:

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        string monitorPath = "/tmp/test_inotify"; // 要监控的目录,不存在可以先创建
        if (!Directory.Exists(monitorPath))
        {
            Directory.CreateDirectory(monitorPath);
        }

        try
        {
            // 初始化inotify实例
            int inotifyFd = InotifyHelper.InitInotify();
            // 添加监控,监听创建、修改、删除事件
            int wd = InotifyHelper.AddWatch(inotifyFd, monitorPath, 
                InotifyHelper.IN_CREATE | InotifyHelper.IN_MODIFY | InotifyHelper.IN_DELETE);

            Console.WriteLine($"开始监控目录:{monitorPath}");

            // 循环读取事件
            while (true)
            {
                var events = InotifyHelper.ReadEvents(inotifyFd);
                foreach (var evt in events)
                {
                    string eventType = evt.EventMask switch
                    {
                        var m when (m & InotifyHelper.IN_CREATE) != 0 => "文件创建",
                        var m when (m & InotifyHelper.IN_MODIFY) != 0 => "文件修改",
                        var m when (m & InotifyHelper.IN_DELETE) != 0 => "文件删除",
                        _ => $"其他事件,掩码:{evt.EventMask}"
                    };
                    Console.WriteLine($"[{DateTime.Now}] 事件类型:{eventType},文件名称:{evt.Name}");
                }
                Thread.Sleep(100); // 降低CPU占用
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"监控发生错误:{ex.Message}");
        }
    }
}

注意事项

  • 监控的目录或文件需要有对应的读取权限,否则添加监控会失败。
  • Inotify的内核缓冲区大小有限,如果事件产生速度过快,可能会出现事件丢失的情况,需要根据业务场景调整缓冲区大小。
  • 如果需要监控子目录下的文件变更,需要递归添加子目录的监控,Inotify本身不会自动监控子目录。
  • 程序退出时一定要关闭inotify实例的文件描述符,避免资源泄漏。

C#LinuxInotify文件监控修改时间:2026-07-24 10:06:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。