关于通过网络使用 Linux 监视文件更改的思考关于通过网络使用 Linux 监视文件更改的思考关于通过网络使用 Linux 监视文件更改的思考关于通过网络使用 Linux 监视文件更改的思考
  • 文章
  • 正则表达式
    • 工具
  • 登录
找到的结果: {phrase} (显示: {results_count} 共: {results_count_total})
显示: {results_count} 共: {results_count_total}

加载更多搜索结果...

搜索范围
模糊匹配
搜索标题
搜索内容
发表 admin at 2025年2月28日
类别
  • 未分类
标签

关于通过网络使用 Linux 监视文件更改的思考

在此页

  1. 这是为什么?
  2. 一个可能的场景:
  3. 注意事项:

通过众所周知的机制 inotify,可以使用 Linux 监视目录的更改。使用 inotify 可以在目录上设置监视,将其配置为监视内容上的事件,并且当发生某些事情时,您将在文件描述符上接收消息。当目录位于本地存储(如硬盘驱动器、SSD 或 USB 驱动器)上时,这非常有效,但当目录位于网络文件系统上且存储位于另一台计算机上时,这就不够用了。在同一个目录中工作的另一个用户,通过同一个或另一个文件系统连接,可以删除一个文件,而你在该文件上设置的手表将不会收到通知。

为什么是这样?

按照设计,inotify 获取操作的结果(如 mkdir 或 chmod),但 watch 所在的文件系统类型是未知的(黑盒)以进行 inotify。文件系统不“知道”已设置监视,因此无法采取正确的操作,例如通知远程主机有人想要监视目录。

只要您是唯一的用户,就没有问题。当有更多用户在您要观看的目录中工作时,这就会成为一个问题。

您可以将此行为与公共图书馆进行比较。当您是唯一的用户时,您可以知道哪些书可用,哪些不可用,因为您知道自己借了哪些书。当您不是唯一的用户时,这不再是可能的,因为有更多的用户借书。

在这种情况下,图书馆应该有人管理任何用户借来的东西(这是通常的情况),你必须联系这个人来了解一本书是否可用。这就像要求某人在当前不存在的书籍再次可用时通知您。

现在,这种与图书馆联系以通知您的做法不适用于 Linux,当然,图书馆是远程存储,而服务器是在图书馆工作的“某人”。

使它与 Linux 一起工作就是让远程服务器收到通知,手表已设置。

实际上,CIFS 和最新版本的 NFS 等文件系统支持向服务器发送监视:对于内核 4.1.2 的 fs/cifs/cifssmb.c 的第 6438 行的 CIFS,此 SMB 消息 (NT_TRANSACT_NOTIFY_CHANGE) 被注释掉但仍然存在。将其注释掉的原因是它与 dnotify 一起工作,它在很长一段时间内都不是 Linux 的默认 fsnotify 系统。


可以通过内核空间在具有网络文件系统和 FUSE 的 Linux 上进行 watch 转发。

最近我尝试用 FUSE 实现这个“将手表转发到服务器”。我不得不打补丁:

FUSE 内核模块在收到 fsnotify 通知后采取行动。我引入了一个新的操作代码
FUSE_FSNOTIFY 内核模块将 inodenumber 和掩码一起发送到用户空间守护进程。

FUSE 库接收和处理 fs 事件并将这些事件报告回 VFS。

仔细看看它是如何工作的,当用户空间文件系统在其后端成功设置 watch 时(注意它也可能回复 ENOSYS),后端可以随时向 watch 发送事件,直到手表被删除。这个事件怎么办?

一个可能的场景:

引入一个额外的 FUSE 操作码 FUSE_FSNOTIFY_EVENT,将从后端协议接收到的事件中的掩码转换为 fsnotify 可以理解的内容,并使用新的操作码、手表的索引节点、条目的名称将其发送回 FUSE 模块,以及翻译后的掩码。轮到 FUSE 模块将它发送到 fsnotify 子系统,该子系统通知监听器(inotify 和/或 fanotify),其中提供事件在后端的信息。 (需要一个额外的事件标志,例如用于 inotify 事件掩码 IN_REMOTE,用于 fanotify FAN_REMOTE)。如何处理这些信息取决于听众。本地 VFS 可能已经或可能不是最新的。

笔记:

将来自后端的掩码转换为 fsnotify 理解的东西可能非常容易,也可能不那么容易,具体取决于事件。在监视目录中创建(或删除)条目等基本事件很简单(分别为 FS_CREATE 和 FS_DELETE),所有者的更改也不那么困难(FS_ATTRIB),但类似于扩展属性(SMB 使用很多)只能翻译成通用的东西,如 FS_ATTRIB。

FUSE 模块应该检查 watch 和/或 inode 是否仍然有效,以及 watch 的掩码是否适用于 eventmask。

需要额外的掩码位 IN_REMOTE(用于 inotify)和 FAN_REMOTE(用于 fanotify)。

应避免双重信息。这很棘手。例如,在 watch 所在的同一主机上的 watched 目录中创建文件。当此操作成功时,这将导致一个 fsnotify 事件 FS_CREATE,它还会创建一个 FS_CREATE | FS_REMOTE事件,由于在后端成功执行操作,导致此消息(从后端→fuse库→FUSE内核模块→fsnotify子系统→inotify和/或fanotify)。

解决这个问题的一种方法是要求后端只发送由其他人发起的事件。对于后端,将 FS 事件的发起者(主机)与建立连接的主机进行比较非常简单。

另一种解决方案是将上报的事件与熔断库和FUSE模块中的本地缓存进行比较。对于创建文件的示例,库(和 FUSE 模块)应检查条目是否已存在于监视目录中。如果不是,则不是由该主机发起的。对于删除,这是类似的。

对于其他事件,如写入文件或更改所有者,此方法是不够的,有关远程更改内容的附加信息(如新大小、新所有者)必须包含在远程主机发送的消息中。

如果后端没有提供该信息,另一种解决方案是让负责代表客户端监视 FS 事件的守护进程维护最近本地事件的缓存。如果报告了远程事件,并且在缓存中没有找到本地等效事件,则它是由另一台主机发起的。这可能会变得棘手,因为事件是使用特定用户的连接报告的,其他用户可能会或可能不会被授权接收事件。这个缓存有多大?

我在上面使用了 FUSE,我认为它与其他文件系统(如 CIFS 和 NFS)类似。

哦,是的,还有另一种选择:每 5 秒左右轮询一次。

史蒂夫邦

©2015-2025 艾丽卡 support@alaica.com