一般来说,从文件系统中获得文件变化信息,调用操作系统提供的 API 即可。Windows 操作系统上有个名为 ReadDirectoryChangesW 的 API 接口,只要监视一个目录路径就可以获得包括其子目录下的所有文件变化信息,简单高效;接口的支持度也很广,现有主流的 Windows 操作系统都支持,往前还可以追溯到 Windows 2000。对码农来说,能提供稳定有效且好用的 API 的系统就是好系统。而本文将讨论 iGuard 网页防篡改系统在 Linux 上获取文件变化信息的方法及从 NFS 网络文件系统中获取文件变化时遇到的困难和心得。

▲ReadDirectoryChangesW

在 Linux 系统上获取文件变更信息,就没有这样的好运了,想要一个像 Windows 上一样提供 ReadDirectoryChangesW 功能的 API,似乎是一种奢望。Linux 内核版本 2.4.0 (2001) 中引入了一个叫 dnotify 的目录检测机制,不怎么好用;内核 2.6.13 (2005) 引入了新方法 inotfiy,但它与 ReadDirectoryChangesW 相比还是差一大截,一次调用只能监视当前目录下的文件变更,子目录里的变更则是无法感知的。如果要获取整个目录下的所有文件变化,应用程序需要遍历整个目录,并把所有的目录监视起来。通过 inotify 接口获得一个目录创建事件时,需要把这个新建的目录及时添加到监视列表,才有可能获得新目录下的文件变化。应用程序处理变化信息较慢时,在把新建目录添加到监视列表前,新目录下的文件事件是极有可能丢失的。对于一个巨型文件系统来说,遍历出所有的目录也是件费事耗资源的任务。如此看来,inotify 机制并不完善,小规模文件数量的场景尚能胜任,像集约化平台,文件规模太庞大,就难以满足了。

▲dnotify

▲inotify

随着 Linux 系统的演化,获取文件变化信息的手段也在发展,但一直不完善,inotify 的缺陷同样表现在 NFS 系统上。NFS 系统是天存信息的用户中一种常见的应用场景,它共享海量文件。我们的 iGuard 网页防篡改系统在 NFS 上需要一种可靠的获取文件变化的手段,来保障安全业务的 7×24h 运转。鉴于 Linux 系统公开的 API 似乎不能满足我们的要求,只有另辟蹊径。幸好 Linux 是开源的,没有现成的就改一个出来。

我们的改造目标指向了 NFS 系统的服务模块 nfsd。在 Linux 内核源代码树下的文件系统 fs 目录中很容易找到 nfsd 模块的同名目录。在 Linux 系统中,NFS 服务透过虚拟文件系统 VFS 接口来访问真实的文件系统,文件的新建、改写、改名和删除等动作是非常清晰的。我们很快就把这些文件更改相关的事件传递出来并为我所用。

最初,我们的这种 nfsd 解决方案和 iGuard 网页防篡改系统看起来是可以一起工作的,在用户生产环境下可以稳定地跑上几周几个月,基本上没有问题。随着集约化平台的兴起,大量网站集中到统一的管理平台下进行内容编辑和运维,这样的单一管理平台发布文件的规模每天可达百万级别。我们的 iGuard 系统在超大规模的文件发布量下也暴露出一些问题,文件同步任务阻塞、滞后或者遗漏等;这些问题以前可能没有出现或缺少关注,随着规模变大,这些问题现今被放大了。这些问题中,最难排解的就是文件遗漏,明明磁盘文件已经更新,但系统就是没有把文件内容同步到远端。后来追查发现,在某些情况下,我们无法获得 NFS 服务所写文件对象的完整文件路径,进而无法输出对应文件的变更消息。

在 Linux 文件系统中,inodedentry 是两个重要的数据结构 。前者对应于磁盘文件的元数据 (类型、尺寸、权限等,但不包括文件路径) 和文件数据块索引,每个 inode 都有一个编号,在文件系统中是唯一的;后者是文件系统运行过程中创建的内存对象,组合成目录项高速缓存 dcache,每个 dentry 对应文件路径上的一个节点并和一个 inode 相关联,目录树由这些 dentry 组成,可以通过遍历目录树来获取文件路径,dentry 可以被视作某种缓存信息,让文件系统运行得更快更高效。

通过 NFS 对外提供文件访问的系统需要符合文件系统可导出规范,这个规范在 Linux 的内核文档 Making Filesystems Exportable 中有简要说明,其中提到 NFS 通信协议中使用文件句柄来标识文件,而不是平时所想象的按文件路径来定位文件。这个句柄信息跟符合可导出规范的文件系统相关,包含 inode 的编号、文件系统标识等信息。这里可以看出,我们需要的文件变化消息是基于文件路径,而 NFS 操作文件是基于这种文件句柄,这里就存在从文件句柄到文件路径的转译过程。

在一般情况下,这个转译过程是正常的,每一个 NFS 文件句柄都可以在 dcache 中找到对应的文件。文档 Making Filesystems Exportable 中还提到 dcache 构建中的 2 个注意事项,大致是:

  • dcache 包含的对象有时候是没有合适前缀的节点 (可以理解为孤立的),该节点没有与根节点相连。
  • 新遍历出来的节点可能是已存在于 dcache 的孤立节点,这种情况需要将孤立节点移动到合适的位置 (可以理解为孤立节点回归到大目录树下)。

这就解释了我们在 NFS 系统中遇到的问题原因——无法获取变更文件的完整路径,因为它没有和根节点相连。我们也能重现问题,在 NFS 服务和客户端工作了一段时间后,重启 NFS 服务器,当 NFS 客户端继续读写曾经访问过的文件时,由于 NFS 服务器上的 dcache 已经复位,客户端请求过来的文件句柄是合法的,并在服务器端形成一个没有合适前缀的节点,这样的节点是无法解析出完整路径的。dcache 毕竟是一个缓存系统,不可能把磁盘上的目录树全部保存到内存,当内存不够用时,dcache 会释放一部分数据并进行内存回收。

NFS 服务的这个问题看似无解,是 NFS 工作模式引发的。为了解决问题,我们尝试采用一种看似笨拙但有效的方法,创造性地构建一张持久化的超大表来跟踪所有的 NFS 文件句柄,记录它们的文件路径信息,通过这张大表转译出那些没头脑的节点。用磁盘空间来换取 NFS 文件句柄的路径检索。方法虽不完美,但我们尽力让 iGauard 网页防篡改系统运行更加完美。(徐品华 | 天存信息)

Ref

  1. Filesystem notification series by Michael Kerrisk
  2. Making Filesystems Exportable

iGuard和NFS文件同步的解决方案的更多相关文章

  1. php中并发读写文件冲突的解决方案(文件锁应用示例)

    PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适 ...

  2. Sersync实现触发式文件同步 替代inotify和rsync

    Sersync实现触发式文件同步 替代inotify和rsync Pyinotify是一个Python模块,用来监测文件系统的变化. Pyinotify依赖于Linux内核的功能—inotify(内核 ...

  3. 挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务

    挑战以Dropbox为代表的传统“同步网盘”,Seafile推出“分布式文件同步技术”打造的私有云服务#36氪开放日# 其他 JasonZheng • 2012-04-07 15:14 来自36氪开放 ...

  4. Seafile 推出 “分布式文件同步技术” 打造的私有云服务

    近两年来 Dropbox 等云储存服务迅速窜红,各大巨头纷纷推出自家的云储存服务(苹果的 iCloud, 微软的 SkyDrive, Google 即将推出的 GDrive),国内也有类似的服务(金山 ...

  5. rsync+sersync+inotify实现服务器间文件同步之一

    rsync+sersync+inotify实现服务器间文件同步之一:rsync安装配置 2013年12月14日 ⁄ Linux管理, 服务器集群技术 ⁄ 共 4925字 ⁄ rsync+sersync ...

  6. 负载均衡配置下的不同服务器【Linux】文件同步问题

    负载均衡配置下的不同服务器[Linux]文件同步问题2017年04月13日 22:04:28 守望dfdfdf 阅读数:2468 标签: linux负载均衡服务器 更多个人分类: 工作 问题编辑版权声 ...

  7. 公司和家里代码文件同步方案: (git和dropbox实现)

    公司和家里代码文件同步方案: (git和dropbox实现) 参与公司福利购入了有补贴的macbook pro后, 就不用上下班背着电脑了. 但是也出现了另外一问题: 家里和公司代码同步的问题 公司有 ...

  8. java大文件上传解决方案

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  9. rsync nfs 实时同步,结合实战

    目录 rsync nfs 实时同步,实战 一.部署rsync服务端(backup) 二.部署rsync客户端(nfs,web01) 三.部署web代码(web01) 四.NFS服务端部署(nfs) 五 ...

随机推荐

  1. CF1032G Chattering

    CF1032G Chattering 题意 思路 对于每一个位置,它转移的范围是确定的. 对于一段可以走到的区间,我们可以求出区间中所有点再能走到区间范围. 于是这个就可以倍增进行转移. 如何快速求出 ...

  2. 流暢的python學習-3

    一.文件操作 #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Apr 23 20:59 ...

  3. string子串匹配(用string自带函数,不涉及char数组转换)

    using namespace std; #include <iostream> #include<string> //第1种,用string自带的s.subdtr()截取任意 ...

  4. TensorFlow模型部署到服务器---TensorFlow2.0

    前言 ​ 当一个TensorFlow模型训练出来的时候,为了投入到实际应用,所以就需要部署到服务器上.由于我本次所做的项目是一个javaweb的图像识别项目.所有我就想去寻找一下java调用Tenso ...

  5. noip模拟33[进阶啦啦啦]

    noip模拟33 solutions 不知道该咋说,这场考试其实是我这三四场以来最最最最最顺心的一场了 为啥呢?因为我这回思考有很多结果,得到了脑袋的回复 就是你想了半个小时就有了一点点头绪,那感觉就 ...

  6. Python实现猜数字游戏

    Python中实现猜数字游戏代码如下: import random # 引入随机数标准库-random # 定义数字上下限和最大游戏次数 min_num = 1 max_num = 10 guess_ ...

  7. Python中strip()、lstrip()、rstrip()函数的用法

    Python中使用函数strip().lstrip().rstrip()来剔除字符串前后的特定字符 函数语法为:str.strip(chars) 返回值是一个新的字符串,不更改源字符串 其中,参数ch ...

  8. 小白学vue第四天,从入门到放弃(vue指令的使用加高阶函数)

    v-on修饰符的使用 .stop 阻止事件冒泡 调用  stopPropagation() .prevent 阻止默认事件 调用 event.preventDefault() .keyCode 键盘事 ...

  9. Mol Cell | 张令强/贺福初/魏文毅/刘翠华揭示线性泛素化调控血管生成新机制

    景杰学术 | 报道 泛素化修饰作为主要的蛋白质翻译后修饰之一,与细胞周期.应激反应.信号传导和DNA损伤修复等几乎所有的生命活动密切相关[1].泛素分子通常含有7个赖氨酸残基,通过这些残基可以和其他泛 ...

  10. C++水仙花 (如:153 = 1*1*1 + 5*5*5 + 3*3*3)

    1 #include <iostream> 2 #include <ctime> 3 using namespace std; 4 5 int main() 6 { 7 int ...