一、inotify概述

1、inotify简介

   inotify是一种强大的细粒度的异步的文件系统时间监控机制,linux内核从2.6.13版本起,加入了对inotify的支持

   inotify为用户态监视文件系统的变化提供了强大的支持,允许监控程序打开一个独立文件描述符,并针对事件集监控一个或者多个文件,例如打开、关闭、移动/重命名、删除、创建或者改变属性。   

   inotify 的前身是dnotify

    

2、inotify取代dnotify的原因有哪些呢?

 1)Inotify使用一个独立的文件描述符,而dnotify需要为每个受监控的目录打开一个文件描述符。当你同时监控多个目录时,就会消耗大量的资源,因此成本会非常高,而且还会遇到每个进程文件描述符数量的限制。

 2)Inotify所使用的文件描述符可以通过系统调用获得,并且没有相关设备或者文件。而dnotify在使用时,文件描述符会锁定目录,导致设备无法卸载,这是可移动媒体的一个典型问题。在使用inotify时,如果正在监控被卸载的文件系统上的文件,那么watch会被自动移除,并且您会接收到一个umount事件。

 3)dnotify的设计过于复杂。inotify能够监视文件或者目录,而使用dnotify时,监控粒度只停留于目录级别。为了使dnotify能够进行更细粒度的监控,应用程序编程人员必须为每个受监控的目录保留一个stat结构的缓存。该用户空间的stat结构缓存需要用来明确确定当接收到通知信号时,目录发生了什么变化。当获得通知信号时,生成stat结构列表并与最新的状态相比较。很明显,这种技术是不太理想的。

 4)inotify 使用文件描述符作为基本接口,允许应用程序开发者使用 select 和 poll来监控设备,并且允许高效的多路I/O和与Glib的mainloop的集成。相反,dnotify所使用的SIGIO常常使程序员头疼并且感觉不太优雅。在2.6.25内核中inotify还添加了Signal-drive I.O通知功能。

  inotify是内核中的功能,因此为了让用户态更方便的去使用到它,它通过提供一个更优雅的API来解决了这些问题。该API使用最少的文件描述符,并确保更细粒度的监控,与inotify的通信是通过设备节点提供的。

3、列举inotify中常用的函数

inotify_init()  

   用于创建一个inotify实例的系统调用,并返回一个指向该实例的文件描述符

inotify_init1()

   与inotify_init()相似,并带有附加标志。如果这些附加标志没有指定,将采用与inotify_init相同的值

inotify_add_watch()

    增加对文件或者目录的监控,并指定需要监控哪些事件。标志用于控制是否将事件添加到已有的监控中,是否只有路径代表一个目录才进行监控,是否要追踪符号链接,是否进行一次性监控,当首次事件出现后就停止监控

inotify_rm_watch()

    从监控列表中移出监控项目

read()

   读取包含一个或者多个事件信息的缓存

close()

   关闭文件描述符,并且移除所有在该描述符上的所有监控。当关于某实例的所有文件描述符都关闭时,资源和下层对象都将释放,以供内核再次使用

因此,一个典型的监控程序需要进行如下功能:

 1)使用inotify_init打开一个文件描述符

 2)添加一个或者多个监控

 3)等待事件

 4)处理事件,然后返回并等待更多事件

 5)当监控不再活动时,或者接到某个信号之后,关闭文件描述符,清空,然后退出。

可监控的事件:

  作为文件系统事件监控机制,到底有哪些事件能够被监控呢?

  下面为大家列举,inotify所能监控的事件标志如下:

IN_ACCESS   被监控项目或者被监控目录中的条目被访问过。例如,一个打开的文件被读取。
IN_MODIFY   被监控项目或者被监控目录中的条目被修改过。例如,一个打开的文件被修改。
IN_ATTRIB     被监控项目或者被监控目录中条目的元数据被修改过。例如,时间戳或者许可被修改。
IN_CLOSE_WRITE        一个打开的,等待写入的文件或目录被关闭。
IN_CLOSE_NOWRITE  一个以只读方式打开的文件或目录被关闭。
IN_CLOSE    一个掩码,可以很便捷地对前面提到的两个关闭事件(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)进行逻辑操作。
IN_OPEN    文件或目录被打开。
IN_MOVED_FROM     被监控项目或者被监控目录中的条目被移出监控区域。该事件还包含一个 cookie 来实现 IN_MOVED_FROM 与 IN_MOVED_TO 的关联。
IN_MOVED_TO          文件或目录被移入监控区域。该事件包含一个针对 IN_MOVED_FROM 的 cookie。如果文件或目录只是被重命名,将能看到这两个事件,如果它只是被移入或移出非监控区域,将只能看到一个事件。如果移动或重命名一个被监控项目,监控将继续进行。参见下面的 IN_MOVE-SELF。
IN_MOVE    可以很便捷地对前面提到的两个移动事件(IN_MOVED_FROM | IN_MOVED_TO)进行逻辑操作的掩码。
IN_CREATE 在被监控目录中创建了子目录或文件。
IN_DELETE  被监控目录中有子目录或文件被删除。
IN_DELETE_SELF  被监控项目本身被删除。监控终止,并且将收到一个 IN_IGNORED 事件。
IN_MOVE_SELF    监控项目本身被移动。

  除了事件标志以外,还可以在 inotify 头文件(/usr/include/sys/inotify.h)中找到其他几个标志。

二、inotify-tools

1、inotify-tools简介

   上面,简单学习了inotify 原理相关的东西。其中,inotify提供了面向用户态API 。那么 inotify在用户态的表现形式是什么呢?

   这就需要用到一个工具inotify-tools 。

   inotify-tools是为linux下inotify文件监控工具提供的一套c的开发接口库函数,同时还提供了一系列的命令行工具,这些工具可以用来监控文件系统的事件。inotify-tools是用c编写的,除了要求内核支持 inotify外,不依赖于其他。

2、安装inotify-tools

 1)查看OS的内核是否支持inotify

[root@Node5 ~]# uname -r2.6.32-431.el6.x86_64[root@Node5 ~]# ls /proc/sys/fs/inotify/max_queued_events  max_user_instances  max_user_watches

inotify的默认内核参数详解:

/proc/sys/fs/inotify/max_queued_events
    
默认值: 16384
    
该文件中的值为调用inotify_init时分配给inotify instance中可排队的event的数目的最大值,超出这个值得事件被丢弃,但会触发IN_Q_OVERFLOW事件
/proc/sys/fs/inotify/max_user_instances
    
默认值: 128
    
指定了每一个real user ID可创建的inotify instatnces的数量上限
/proc/sys/fs/inotify/max_user_watches
    
默认值: 8192
    
指定了每个inotify instance相关联的watches的上限,也就是每一个inotify实例可监控的最大目录数。如果监控的文件数目巨大,需要根据实际情况适当增加此值得大小。
 
注意: 
    
max_queued_events 是 Inotify 管理的队列的最大长度,文件系统变化越频繁,这个值就应该越大!如果你在日志中看到Event Queue Overflow,说明max_queued_events太小需要调整参数后再次使用

 2)安装inotify-tools

[root@Node5 src]# ls inotify-tools-3.13.tar.gz inotify-tools-3.13.tar.gz[root@Node5 src]# tar xf inotify-tools-3.13.tar.gz[root@Node5 src]# cd inotify-tools-3.13[root@Node5 inotify-tools-3.13]# lsaclocal.m4  config.guess  configure     depcomp     libinotifytools  Makefile.in  NEWSAUTHORS     config.h.in   configure.ac  INSTALL     ltmain.sh        man          READMEChangeLog   config.sub    COPYING       install-sh  Makefile.am      missing      src[root@Node5 inotify-tools-3.13]# ./configure[root@Node5 inotify-tools-3.13]# make && make install

在编译安装 inotify-tools后生成的文件:

[root@Node5 inotify-tools-3.13]# ls /usr/local/bininotifywait  inotifywatch[root@Node5 inotify-tools-3.13]# ls /usr/local/liblibinotifytools.a   libinotifytools.so    libinotifytools.so.0.4.1libinotifytools.la  libinotifytools.so.0

 inotifywait 

      仅执行阻塞,等待inotify事件,你可以使用它来监控任何一组文件和目录,或监控整个目录树(目录、子目录、子目录的子目录等等),并且可以结合shell脚本,更好的使用inotifywait。

  inotifywatch 

       用来收集关于被监视的文件系统的统计数据,包括每个inotify事件发生多少次。

三、inotify的用法

1、inotifywait

语法格式:

  inotifywait [-hcmrq] [-e ] [-t ] [--format ] [--timefmt ] [ ... ]

选项:  

-h,–help    
# 输出帮助信息
@     
# 排除不需要监视的文件,可以是相对路径,也可以是绝对路径
–fromfile   
# 从文件读取需要监视的文件或排除的文件,一个文件一行,排除的文件以@开头
-m,–monitor 
# 接收到一个事情而不退出,无限期地执行。默认行为是接收到一个事情后立即退出
-d,–daemon  
# 跟–monitor一样,除了是在后台运行,需要指定 –outfile把事情输出到一个文件。也意味着使用了–syslog
-o,–outfile 
# 输出事情到一个文件而不是标准输出。
-s,–syslog  
# 输出错误信息到系统日志
-r,–recursive 
# 监视一个目录下的所有子目录。
-q,–quiet   
# 指定一次,不会输出详细信息,指定二次,除了致命错误,不会输出任何信息。
–exclude    
# 正则匹配需要排除的文件,大小写敏感。
–excludei   
# 正则匹配需要排除的文件,忽略大小写。
-t,–timeout
# 设置超时时间,如果为0,则无限期地执行下去。
-e,–event   
# 指定监视的事件。
-c,–csv     
# 输出csv格式。
-–timefmt    
# 指定时间格式,用于-–format选项中的%T格式。
-–
format     
# 指定输出格式。
   
%w 表示发生事件的目录
   
%f 表示发生事件的文件
   
%e 表示发生的事件
   
%Xe 事件以“X”分隔

   %T 使用由–timefmt定义的时间格式

下面是一些可监听事件,针对-e选项使用:

access

文件读取

modify

文件更改。

attrib

文件属性更改,如权限,时间戳等。

close_write

以可写模式打开的文件被关闭,不代表此文件一定已经写入数据。

close_nowrite

以只读模式打开的文件被关闭。

close

文件被关闭,不管它是如何打开的。

open

文件打开。

moved_to

一个文件或目录移动到监听的目录,即使是在同一目录内移动,此事件也触发。

moved_from

一个文件或目录移出监听的目录,即使是在同一目录内移动,此事件也触发。

move

包括moved_to和 moved_from

move_self

文件或目录被移除,之后不再监听此文件或目录。

create

文件或目录创建

delete

文件或目录删除

delete_self

文件或目录移除,之后不再监听此文件或目录

unmount

文件系统取消挂载,之后不再监听此文件系统。

 1)实时监控/etc目录的所有事件(包括文件的访问,写入,修改,删除等)

[root@Node5 ~]# inotifywait -rm /tmp Setting up watches.  Beware: since -r was given, this may take a while!Watches established.# 此时打开另一个终端,操作/tmp及子文件这里就会显示相应的事件信息了/tmp/ OPEN,ISDIR /tmp/ CLOSE_NOWRITE,CLOSE,ISDIR /tmp/ OPEN sb.txt/tmp/ ACCESS sb.txt/tmp/ CLOSE_NOWRITE,CLOSE sb.txt/tmp/ CREATE aaa/tmp/ OPEN aaa/tmp/ ATTRIB aaa/tmp/ CLOSE_WRITE,CLOSE aaa/tmp/ DELETE aaa

 2)实时监控/home目录的文件或目录创建,修改和删除相关事件

[root@Node5 ~]# inotifywait -e create,modify,delete -rm /homeSetting up watches.  Beware: since -r was given, this may take a while!Watches established./home/ CREATE test.file/home/ MODIFY test.file/home/ DELETE test.file

 3)实时监控/etc/passwd的文件修改,删除和权限相关事件,并且要求指定输出格式为27/06/14 16:12 /etc/passwd ATTRIB。

[root@Node5 ~]# inotifywait -rm --timefmt '%Y%m%d %H:%M:%S' --format '%T %w%f %e' -e  modify,delete,attrib  /etc/passwdSetting up watches.  Beware: since -r was given, this may take a while!Watches established.20170210 20:16:48 /etc/passwd ATTRIB

 4)写一个脚本实现对/data/web目录进行监控,监控文件删除,修改,创建和权限相关事件,并且要求将监控信息写入/var/log/web_watch.log。要求日志条目要清晰明了,能突显文件路径、事件名和时间。

[root@node5 ~]# cat web_watch.sh#!/bin/bashinotifywait -mrq --timefmt '%Y/%m/%d %H:%M' --format  '%T %w%f %e' --event delete,modify,create,attrib  /data/web | while read  date time file event  do      case $event in          MODIFY|CREATE|MOVE|MODIFY,ISDIR|CREATE,ISDIR|MODIFY,ISDIR)                  echo $event'-'$file'-'$date'-'$time >> /var/log/web_watch.log              ;;             MOVED_FROM|MOVED_FROM,ISDIR|DELETE|DELETE,ISDIR)                  echo $event'-'$file'-'$date'-'$time >> /var/log/web_watch.log              ;;      esac  done[root@rsync-client ~]# cat /var/log/web_watch.log CREATE-/data/web/a-14/06/27-16:21CREATE-/data/web/aa-14/06/27-16:21CREATE-/data/web/aaaa-14/06/27-16:24CREATE-/data/web/aaaaa-14/06/27-16:24

2、inotifywatch

语法格式:

  inotifywatch [-hvzrqf] [-e] [-t] [-a] [-d] [...]

选项:

-h,–help    
# 输出帮助信息
-
v
,–verbose 
# 输出详细信息
@             
# 排除不需要监视的文件,可以是相对路径,也可以是绝对路径。
–fromfile    
# 从文件读取需要监视的文件或排除的文件,一个文件一行,排除的文件以@开头。
-z,–zero    
# 输出表格的行和列,即使元素为空
–exclude     
# 正则匹配需要排除的文件,大小写敏感。
–excludei    
# 正则匹配需要排除的文件,忽略大小写。
-r,–recursive  
# 监视一个目录下的所有子目录。
-t,–timeout    
# 设置超时时间
-e,–event      
# 只监听指定的事件。
-a,–ascending  
# 以指定事件升序排列。

-d,–descending # 以指定事件降序排列

1、统计/home目录所在文件系统发生的事件次数

[root@Node5 ~]# inotifywatch -v -e create,delete -t 30 -r /homeEstablishing watches...Setting up watch(es) on /homeOK, /home is now being watched.Total of 9 watches.Finished establishing watches, now collecting statistics.Will listen for events for 30 seconds.# 此时打开新的终端,操作/home,等监控的30秒时间到了之后,他就会显示出上面的事件次数报告!total  create  delete  filename3      2       1       /home/1      1       0       /home/aaa/

四、rsync + inotify-tools

   上一篇博文中,我们学习了rsync的诸多特性以及它所支持的四种模式。作为一个镜像备份工具,可以说rsync做的很出色。可是,随着应用系统规模的不断扩大,我们对数据的安全性和可靠性方面的需求也越来越高!Rsync在高端业务系统中的不足也逐渐暴露了出来。

   首先,rsync在同步数据时,需要扫描所有文件后才进行比对,然后再进行差量传输。如果文件数量达到了百万甚至千万量级,扫描所有文件将是非常耗时的一个操作,并且往往发生变化的是其中很小的一部分,那么这将是非常低效的方式。

   其次,rsync不能实时的去监测和同步数据。虽然我们使用crontab的方式,加上rsync自身以守护进程启动的方式实现触发同步,但是两次触发动作一定会有时间差。受限于crontab最短也是1分钟,因此这就导致了服务端和客户端数据可能出现不一致,更无法在应用故障时做到数据的完全恢复。

   为了满足这方面的需求,我们就结合了linux文件系统事件监控机制这样一个系统特性(即inotify),通过使用工具inotify-tools,整合出了rsync+inotify这样的一个技术架构,来实现数据实时同步的功能!

   

   我们知道rsync可以实现推送和拉取,而inotify-tools借助内核的inotify机制实现了文件的实时监控。因此,借助这个思路,我们可以通过使用shell脚本,调整inotifywait的输出格式,然后借助inotifywait的监控文件或目录实时变化去通知rsync做相应的推送或者拉取操作!

  我们就用上一篇博客的环境来接着部署rsync+inotify-tools架构吧!

  node1为rsync服务端,node5为rsync客户端并安装inotify-tools,当node5上指定目录下的文件发生改变时,触发rsync将此目录的文件推送到node1上同步文件。

1、实时备份脚本

下面,我就贴出来,我所使用的脚本!

[root@Node5 ~]# cat auto_rsync.sh #!/bin/bash####inotify+rsync实现数据实时同步的脚本文件server=192.168.10.1src=/tmp/src/dst=mydatauser=myuserlog_file=/tmp/rsync.log###把命令产生的信息管道传到while循序语句中inotifywait -mrq --timefmt '%Y%m%d %H:%M:%S' --format '%T %w%f %e' -e  modify,delete,create,move,attrib  $src | while read logdo rsync -azP --delete $src $user@$server::$dst --password-file=/etc/rsync.passwd &> /dev/nullecho  "$log was rsynced" >> $log_filedone

测试:

   操作/tmp/src中的文件,

[root@Node5 src]# ls105  105.txt  111  abc  fstab  fstab.txt  test

查看日志文件:

[root@Node5 tmp]# cat rsync.log 20170110 19:42:29 /tmp/src/fstab ATTRIB was rsynced20170110 19:42:49 /tmp/src/abc.txt MOVED_FROM was rsynced20170110 19:42:49 /tmp/src/aaa.sh MOVED_TO was rsynced20170110 19:43:54 /tmp/src/aaa.sh MOVED_FROM was rsynced20170110 19:46:33 /tmp/src/ ATTRIB,ISDIR was rsynced20170110 19:48:42 /tmp/src/abc CREATE was rsynced20170110 19:48:42 /tmp/src/abc ATTRIB was rsynced20170110 19:48:48 /tmp/src/111 CREATE,ISDIR was rsynced20170110 19:51:35 /tmp/src/111 MOVED_FROM,ISDIR was rsynced20170110 19:51:35 /tmp/src/111_test MOVED_TO,ISDIR was rsynced

查看rsync服务器的同步目录:

[root@Node1 mydata]# ls105  105.txt  111  abc  fstab  fstab.txt  test

对比客户端和服务器端文件,不难发现,rsync+inotify架构的整体同步速率还是蛮高的!