当前位置:首页 > 文章列表 > 文章 > linux > Linux fanotify 怎样读取 FID 事件定位文件对象

Linux fanotify 怎样读取 FID 事件定位文件对象

来源:17golang原创 2026-11-01 10:08:16 0浏览 收藏

Linux fanotify 默认更像一个“文件系统事件通知队列”:应用读取事件,看到被访问的对象,然后关闭事件里提供的文件描述符。启用 FAN_REPORT_FID 后,定位对象的方式会改变,事件会附带 FAN_EVENT_INFO_TYPE_FID 信息记录,其中包含文件系统标识 fsid 和不透明的 file handle。

因此,读取 FID 事件的关键不是把 metadata->fd 强行当成普通文件描述符,而是先按 event_len 遍历一条事件,再从元数据后面解析附加记录。官方资料:https://man7.org/linux/man-pages/man7/fanotify.7.html。

想用 FID 定位文件对象,初始化时加入 FAN_REPORT_FID,读取时检查 FAN_EVENT_INFO_TYPE_FID,保存 fsid + file handle 这组身份信息;目录项变更还要根据需要增加目录和目标对象报告标志。

FAN_REPORT_FID 让事件携带对象身份

普通 fanotify 事件常常通过 metadata->fd 让监听器访问被监控的文件或目录。这个 fd 是事件处理资源,读取应用需要在处理完后关闭它。FID 模式则面向“对象身份”而不是“暂时打开的对象”:metadata->fd 可能是 FAN_NOFD,真正有用的信息放在紧随通用元数据之后的可变长度记录里。

FAN_REPORT_FID 对应的记录类型是 FAN_EVENT_INFO_TYPE_FID。它的 fsid 用来表示对象所在的文件系统,file handle 是文件系统返回的不透明句柄。两者应当一起保存,不能只保存句柄而丢掉 fsid,因为句柄的含义依赖它所在的文件系统。

fanotify FAN_REPORT_FID 事件中元数据、FID 记录、fsid 和文件句柄的静态结构关系图
图1:结构说明图,展示 FAN_REPORT_FID 事件中通用元数据与文件对象句柄的关系。

先用通知模式初始化 fanotify

如果目标只是记录访问、打开、创建或删除等事件,先使用默认的 FAN_CLASS_NOTIF。它只提供通知,不负责在访问发生前作出允许或拒绝决定。把 FAN_REPORT_FID 加到初始化标志中后,再通过 fanotify_mark 指定要监控的 inode、挂载点或目录。

#include 
#include 
#include 
#include 

int open_fid_notifier(int watch_fd) {
    // 中文注释:NOTIF 只接收通知;FID 让事件带上文件句柄信息。
    unsigned int flags = FAN_CLASS_NOTIF | FAN_REPORT_FID;
    int fan_fd = fanotify_init(flags, O_RDONLY | O_CLOEXEC);
    if (fan_fd == -1) {
        // 中文注释:初始化失败时保留 errno,调用方可以记录权限或内核配置原因。
        perror("fanotify_init");
        return -1;
    }

    // 中文注释:这里只标记一个 inode,避免示例把监控范围误扩大到整个文件系统。
    uint64_t mask = FAN_OPEN | FAN_ACCESS | FAN_ATTRIB;
    if (fanotify_mark(fan_fd, FAN_MARK_ADD, mask, AT_FDCWD, "/var/lib/app")) {
        // 中文注释:标记失败必须关闭已经创建的 fanotify fd,避免资源泄漏。
        perror("fanotify_mark");
        close(fan_fd);
        return -1;
    }

    // 中文注释:返回的 fd 交给事件循环,后续通过 read 读取整批事件。
    (void)watch_fd;
    return fan_fd;
}

示例把监控范围写成单个目录只是为了突出初始化关系。实际部署时要根据文件系统类型、权限和事件量选择标记范围;FID 句柄不是路径字符串,也不能直接拿来做字符串拼接。

按 event_len 遍历附加 FID 记录

一次 read 可能返回多条事件,所以不能只解析缓冲区开头的一个结构。外层循环使用 FAN_EVENT_OK 判断剩余空间,使用 FAN_EVENT_NEXT 跳到下一条事件。对于开启 FID 的组,event_len 还包含一个或多个附加信息记录。

#include 
#include 

static void read_fid_records(int fan_fd) {
    char buffer[4096];
    ssize_t bytes = read(fan_fd, buffer, sizeof(buffer));
    if (bytes fd)。
        if (meta->fd != FAN_NOFD) {
            close(meta->fd);
        }

        char *info_pos = (char *)meta + meta->metadata_len;
        char *event_end = (char *)meta + meta->event_len;
        while (info_pos + sizeof(struct fanotify_event_info_header) len len > event_end) {
                break;
            }

            if (header->info_type == FAN_EVENT_INFO_TYPE_FID) {
                struct fanotify_event_info_fid *fid =
                    (struct fanotify_event_info_fid *)info_pos;
                // 中文注释:fsid 与可变长度 file handle 是对象身份的组合,不把句柄当路径。
                consume_fsid_and_handle(&fid->fsid, &fid->handle);
            }
            info_pos += header->len;
        }

        // 中文注释:宏会按 event_len 移动指针并扣减剩余字节数。
        meta = FAN_EVENT_NEXT(meta, remaining);
    }
}

这个片段把 consume_fsid_and_handle 留给业务层实现,因为不同文件系统、权限模型和路径解析策略并不相同。要点有三个:先检查记录长度,再判断 info_type,最后把 fsid 与句柄作为一组数据交给后续处理。

目录项变更为什么还需要 TARGET_FID

只报告一个 FID 时,记录通常能说明“哪个对象关联了这次事件”。但对于目录中的创建、删除、移动和重命名,读者往往还想知道“哪个父目录里的哪个子对象发生了变化”。这时可以组合 FAN_REPORT_DIR_FID 和名称报告;如果还要拿到被创建、删除或移动的子对象句柄,则需要把 FAN_REPORT_TARGET_FID 一并纳入兼容性设计。

普通 fanotify 事件文件描述符与 FAN_REPORT_FID 文件句柄记录的静态对比图
图2:边界说明图,对比普通事件文件描述符与 FID 信息记录的读取方式。

这类事件可能携带父目录的 FAN_EVENT_INFO_TYPE_DFID、带名称的目录记录,以及目标对象的 FAN_EVENT_INFO_TYPE_FID。解析代码不要假设附加记录只有一条,也不要依赖记录出现的固定顺序;应当按 header->len 逐条读取并根据 info_type 分派。

不要把 FID 事件当成权限事件

关注点普通通知 / FID 读取权限事件
主要目的知道某个文件对象发生了什么在访问前决定允许或拒绝
初始化类别FAN_CLASS_NOTIFFAN_CLASS_CONTENT 或 FAN_CLASS_PRE_CONTENT
对象信息普通 fd,或 fsid + file handle通常需要处理权限事件中的 fd 并写回响应
处理动作读取、解析、保存或关闭资源向 fanotify fd 写入 FAN_ALLOW 或 FAN_DENY

非特权监听器的能力也有限。man-pages 说明,在较新的内核上,缺少 CAP_SYS_ADMIN 的调用者可以创建受限的 fanotify 组,但不能请求无限队列、不能使用需要权限事件的通知类别,并且需要使用文件句柄报告方式,标记范围也受到限制。因此,FID 模式常用于不希望依赖临时打开 fd 的对象识别场景,但它不等于绕过权限控制。

最小复查:确认读的是记录,不是路径

部署前可以先查看本机内核提供的 man-pages,确认要使用的标志、内核版本边界和能力要求。这个动作只是环境复查,不会把 FID 句柄变成可直接展示的路径。

# 中文注释:查看当前系统安装的 fanotify API 说明。
man 7 fanotify

# 中文注释:查看 fanotify_init 的标志与能力限制。
man 2 fanotify_init

如果日志系统需要稳定关联对象,建议保存事件掩码、进程标识、fsid、句柄原始字节和记录类型;如果只保存一个临时 fd 或一条当时的路径,后续遇到重命名、移动或卸载挂载点时,很容易失去关联。

常见问题

启用 FAN_REPORT_FID 后为什么 metadata.fd 是 FAN_NOFD?

这是 FID 模式的正常表现。对象身份放在附加的 file handle 记录中,读取程序应检查 info_type,而不是把 FAN_NOFD 当成读取失败。

file handle 能直接当成文件路径吗?

不能。它是文件系统相关的不透明句柄,需要结合 fsid 和合适的内核接口处理。文章中的结构解析只能得到对象身份材料,不应把句柄字节直接拼成路径。

只监控目录创建时为什么还看不到子对象句柄?

目录项事件需要区分父目录、名称和目标对象。检查是否同时启用了目录 FID、名称报告以及目标 FID;仅有基础的 FAN_REPORT_FID 不一定会附带被创建或移动的子对象记录。

什么时候继续使用普通事件 fd?

如果业务只需要短时间访问事件对象内容,并且不要求跨重命名或卸载保持对象身份,普通通知 fd 更简单。只有当对象身份、目录项关系或受限监听环境成为重点时,再引入 FID 记录解析。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python ipaddress.collapse_addresses 怎样合并连续网段Python ipaddress.collapse_addresses 怎样合并连续网段
上一篇
Python ipaddress.collapse_addresses 怎样合并连续网段
Linux io_uring 注册缓冲区的生命周期管理
下一篇
Linux io_uring 注册缓冲区的生命周期管理
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    821次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    816次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    905次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    876次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    699次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码