美文网首页
对文件修改时间的深入理解

对文件修改时间的深入理解

作者: 归源 | 来源:发表于2022-04-16 16:20 被阅读0次

如果是你,会怎么比较操作系统上的一个文件是否发生了变化呢?这是我在上一个周的工作里遇到的问题。我想,肯定会有人说,这不是很简单嘛,用 md5sum 去对文件进行校验和计算,如果前后两次的校验和不一致,那么文件就是被修改过的。

诚然,使用 md5sum 对文件求和是一种观察文件是否发生过修改的方式。但倘若使用场景稍微发生一些变化,我们需要观察的是一批文件呢,这些文件或大或小,大的文件近似100G,小的文件或者1M,文件中大小分布不均衡。那么在这样的使用场景中你还会觉得使用 md5sum 对文件求和是一种好办法吗?

或许对于大多数人的使用场景中,绝大部分情况下使用 md5sum 求和是的文件是一些小文件,而且也是单次求和,这样的场景下固然没有什么问题。但倘若连续对10个100G大小的文件进行求和,使用 md5sum 则会出现许多问题。

例如,计算校验和时硬盘的带宽会被消耗殆尽,导致操作系统上出现大量的D进程(不可中断进程),阻塞其他进程的执行。

盖因在操作系统上,文件通常存储在硬盘上,而使用 md5sum 计算校验和时,需要读取文件内容。而这个读取操作首先需要到硬盘上读取文件的位置,然后不断将文件的内容读取出来进行计算。这其中硬盘的读写性能至关重要,差一点的硬盘会在读取上消耗掉大量的时间。而D进程,也往往和磁盘的读写相关。

我刚刚说的是大文件与小文件共存的场景,别以为若是只有小文件就没有上面的问题了。(这里不考虑文件数量比较少的情况),大量的小文件读取难道不会产生大量的磁盘读吗?

实际上,硬盘读在操作系统上上一种非常消耗资源的事,尤其是大量的读,大量占用磁盘带宽并不是一个好的做法。因此,在操作系统设计中包含了 缓存(Cache),缓存的出现不正是为了更好的解决磁盘读的问题吗。

不过,这与我今天要说的并没多大关系,实际上我们也用不到 缓存

还是回到最初的问题上吧,如果是你,会怎么比较操作系统上的一个文件是否发生了变化呢?

既然和文件相关,那么文件自身的属性能否表明文件发生了修改呢?注意,这里我说的是文件内容有没有发生修改。

那么,文件的属性都有什么呢。通过 stat 来看一下吧

#stat testlog
  File: ‘testlog’
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: fd00h/64768d    Inode: 188836      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-04-17 11:44:27.361232934 +0800
Modify: 2022-04-17 11:44:27.361232934 +0800
Change: 2022-04-17 11:44:27.361232934 +0800
 Birth: -

如上所示,文件自身所携带的属性众多。包含 File(文件名)Size(文件大小)Blocks(文件存储所消耗的硬盘块数量)IO Block(操作系统发生一次IO可以查找的硬盘字节数)regular empty file(表明当前文件是一个空文件)DeviceInode(操作系统中的inode编号)Links(操作系统中的引用计数)Access(访问文件的时间)Modify(文件被定义的时间)Change(文件被修改的时间)Birth

看到这些文件属性,或许会觉得直接找到 Change 属性不就说明文件被修改过了吗?但是这真的正确吗?

在说下一步之前,有必要提一下可以修改文件属性的一些命令。touchmvcatgreplessmorevimechochmodchown等等。这部分命令部分修改文件内容,部分修改文件属性,这里不做区分

刚刚说到,直接查找文件的 Change 属性是不是可以证实文件被修改过?让我们重新 touch 一下文件,再看看文件属性的变化吧。

#touch testlog
#stat testlog
  File: ‘testlog’
  Size: 0           Blocks: 0          IO Block: 4096   regular empty file
Device: fd00h/64768d    Inode: 188836      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-04-17 12:02:38.903708993 +0800
Modify: 2022-04-17 12:02:38.903708993 +0800
Change: 2022-04-17 12:02:38.903708993 +0800
 Birth: -

可以看到,文件的三个和时间相关的属性通通发生了变化。但文件的内容并没有发生过修改。类似的场景包含使用 vim 打开一个文件、(未做修改的情况下)保存退出。文件的 Change 属性也会发生变化。

由此可见,我们认为的文件发生变化与操作系统对文件修改事件的定义是不同的。那么,单纯使用文件的 ChangeModify 属性也就无法说明文件是真的被修改过的。

那么,文件的 ChangeModify 两个属性对我们来说就没有用吗?不是的,正如我刚刚使用了 单纯 两个字。

单一的 ChangeModify 属性无法说明文件内容被修改过,那么 Change + Modify + md5sum 能否说明文件被修改过了。当然是可以的。

现在再回到最初的使用场景中,如果存在大量大小不均的大文件和小文件,需要找到其中内容发生修改过的文件呢。

通过记录文件的属性,从中查找前一次文件属性 ChangeModify 的时间戳,和当前的文件时间戳。如果文件的时间戳没有发生变化,那么文件是没有被修改过的。如果前后两次的时间戳不一致,在通过 md5sum 进行校验求和,从而判断文件是否发生过修改。

不可避免的,在一定程度上使用 md5sum 还是会在硬盘上产生大量读,但是通过时间戳的筛选过后,或许并不需要每个文件都去从硬盘上读一次。

或许,在绝大部分场景下,通过 Change + Modify + md5sum 去判断文件是否发生了内容修改已经可以大大降低操作系统的负载了。

当然,最后还是要提一下灵感的来源,inotify 一个针对系统内核事件的监听工具。网上可以看到许多基于 inotify + rsync 实现的远端节点文件同步。

相关文章

  • Typescript: cannot write file '.

    图片引用来源:深入理解typescript: typescript FAQs 解决方案:把对应目录的js文件修改为...

  • Java Class文件修改

    背景:在开发中我们可能需要需要修改Java的class文件来达到修改代码逻辑的目的1.参考书籍《深入理解Java虚...

  • netty源码解析之IO读写(三)

    第二次看netty源码,对netty的理解也更深入了点,修改了不少文章内容。后面有时间再分析一下netty的buf...

  • PyYaml

    安装: 读取 yaml 文件 对文件进行某项修改: 对修改后的文件进行存储:

  • linux-文件目录管理命令

    创建文件 touch:touch 参数 文件名 -a 修改读取时间 -m修改修改时间 -d同时修改创建目录 mkd...

  • 获取文件的时间戳

    php获取文件创建时间、修改时间 filemtime ( string filename ) 返回文件上次被修改的...

  • Git基本命令

    一、Git的文件状态  已修改  在工作目录修改Git文件 已暂存  对修改的文件执行Git暂存操作,将文件纳入暂...

  • 深入理解目标文件

    1. 概述 目标文件是指源代码经过编译后没有被链接的那些中间文件(Linux下的.o)。因为目标文件的内容和结构与...

  • linux 查找02

    find 时间相关-mtime : 指定时间段被修改过的文件-ctime : 指定时间段被修改文件状态的文件-at...

  • 从JVM角度分析方法的重载和重写

    写在前面 本文参照《深入理解Java虚拟机》写作而成,算是对自己理解JVM解释执行class文件的一篇总结吧。 整...

网友评论

      本文标题:对文件修改时间的深入理解

      本文链接:https://www.haomeiwen.com/subject/nwjlertx.html