程序员 Ubuntu 微软 linux java google Firefox apache 云计算 编程 wordpress nginx 开源 Windows Android Python php mysql centos shell

linux 高效的文件系统事件监控 内核级解析方案 inotify

安装inotify-tools (http://inotify-tools.sourceforge.net) 下载源码包

wget http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
tar zxvf inotify-tools-3.14.tar.gz
cd inotify-tools-3.14
./configure --prefix=/usr && make && su -c 'make install'

其它的一些相关软件推荐 https://github.com/rvoicilas/inotify-tools/wiki#related-software

出现这个错误“/usr/local/bin/inotifywait: error while loading shared libraries: libinotifytools.so.0”可以采用以下办法解决:

ln -sv /usr/local/lib/libinotify* /usr/lib/
ln -s /usr/local/lib/libinotifytools.so.0 /usr/lib64/libinotifytools.so.0
cp /usr/lib/libinotifytools.so.0 /usr/local/lib/

Inotify 可监视的文件系统事件

  • IN_ACCESS : 即文件被访问
  • IN_MODIFY : 文件被 write
  • IN_ATTRIB : 文件属性被修改,如 chmodchowntouch
  • IN_CLOSE_WRITE : 可写文件被 close
  • IN_CLOSE_NOWRITE : 不可写文件被 close
  • IN_OPEN : 文件被open
  • IN_MOVED_FROM : 文件被移走,如 mv
  • IN_MOVED_TO : 文件被移来,如 mv、cp
  • IN_CREATE : 创建新文件
  • IN_DELETE : 文件被删除,如 rm
  • IN_DELETE_SELF : 自删除,即一个可执行文件在执行时删除自己
  • IN_MOVE_SELF : 自移动,即一个可执行文件在执行时移动自己
  • IN_UNMOUNT : 宿主文件系统被 umount
  • IN_CLOSE : 文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
  • IN_MOVE : 文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)

注:上面所说的文件也包括目录

Inotify内核版本支持

从kernel 2.6.13开始,Inotify正式并入内核,RHEL5已经支持.看看是否有 /proc/sys/fs/inotify/目录,以确定内核是否支持inotify

#ls -l /proc/sys/fs/inotify/
total 0
-rw-r--r-- 1 root root 0 Oct  9 09:36 max_queued_events
-rw-r--r-- 1 root root 0 Oct  9 09:36 max_user_instances
-rw-r--r-- 1 root root 0 Oct  9 09:36 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的上限 

注意: max_queued_events 是 Inotify 管理的队列的最大长度,文件系统变化越频繁,这个值就应该越大如果你在日志中看到Event Queue Overflow,说明max_queued_events太小需要调整参数后再次使用.

inotifywait 仅执行阻塞,等待 inotify 事件。您可以监控任何一组文件和目录,或监控整个目录树(目录、子目录、子目录的子目录等等)
Shell 脚本中使用 inotifywait。

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

 shell脚本示例

vi /tmp/test.sh
#!/bin/bash
inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format  '%T %w%f %e' --event modify,delete,create,attrib  /home/admin | while read  date time file event
      do
          case $event in
              MODIFY|CREATE|MOVE|MODIFY,ISDIR|CREATE,ISDIR|MODIFY,ISDIR)
                      echo $event'-'$file
                  ;;
      
              MOVED_FROM|MOVED_FROM,ISDIR|DELETE|DELETE,ISDIR)
                      echo $event'-'$file
                  ;;
          esac
      done

执行脚本,结果输出(这里测试删除了一个目录 rm -fr cronolog-1.6.2.bak)

/tmp/test.sh 
DELETE-/home/admin/cronolog-1.6.2.bak/COPYING

我自己使用的一个实例

inotifywait -mrq --timefmt '%Y%m%d %H:%M:%S' --format '%T %e %w %f' -e modify,attrib,move,close_write,create,delete /root/ >/home/inotify.log 

C语言版

#include <unistd.h>
#include <sys/inotify.h>
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <string.h>

#define ERROR(text) error(1, errno, "%s", text)

struct EventMask {
	int        flag;
	const char *name;

};

int freadsome(void *dest, size_t remain, FILE *file)
{
	char *offset = (char*)dest;
	while (remain) {
		int n = fread(offset, 1, remain, file);
		if (n==0) {
			return -1;
		}

		remain -= n;
		offset += n;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	const char *target;
	if (argc == 1) {
		target = ".";
	} else {
		target = argv[1];
	}

	EventMask event_masks[] = {
           {IN_ACCESS        , "IN_ACCESS"}        ,  
           {IN_ATTRIB        , "IN_ATTRIB"}        ,  
           {IN_CLOSE_WRITE   , "IN_CLOSE_WRITE"}   ,  
           {IN_CLOSE_NOWRITE , "IN_CLOSE_NOWRITE"} ,  
           {IN_CREATE        , "IN_CREATE"}        ,  
           {IN_DELETE        , "IN_DELETE"}        ,  
           {IN_DELETE_SELF   , "IN_DELETE_SELF"}   ,  
           {IN_MODIFY        , "IN_MODIFY"}        ,  
           {IN_MOVE_SELF     , "IN_MOVE_SELF"}     ,  
           {IN_MOVED_FROM    , "IN_MOVED_FROM"}    ,  
           {IN_MOVED_TO      , "IN_MOVED_TO"}      ,  
           {IN_OPEN          , "IN_OPEN"}          ,  

           {IN_DONT_FOLLOW   , "IN_DONT_FOLLOW"}   ,  
           {IN_EXCL_UNLINK   , "IN_EXCL_UNLINK"}   ,  
           {IN_MASK_ADD      , "IN_MASK_ADD"}      ,  
           {IN_ONESHOT       , "IN_ONESHOT"}       ,  
           {IN_ONLYDIR       , "IN_ONLYDIR"}       ,  

           {IN_IGNORED       , "IN_IGNORED"}       ,  
           {IN_ISDIR         , "IN_ISDIR"}         ,  
           {IN_Q_OVERFLOW    , "IN_Q_OVERFLOW"}    ,  
           {IN_UNMOUNT       , "IN_UNMOUNT"}       ,  
	};

	int monitor = inotify_init();
	if ( -1 == monitor ) {
		ERROR("monitor");
	}

	int watcher = inotify_add_watch(monitor, target, IN_ALL_EVENTS);
	if ( -1 == watcher  ) {
		ERROR("inotify_add_watch");
	}

	FILE *monitor_file = fdopen(monitor, "r");
	char last_name[1024];
	char name[1024];

	/* event:inotify_event -> name:char[event.len] */
	while (true) {
		inotify_event event;
		if ( -1 == freadsome(&event, sizeof(event), monitor_file) ) {
			ERROR("freadsome");
		}
		if (event.len) {
			freadsome(name, event.len, monitor_file);
		} else {
			sprintf(name, "FD: %dn", event.wd);
		}

		if (strcmp(name, last_name) != 0) {
			puts(name);
			strcpy(last_name, name);
		}

		/* 显示event的mask的含义 */
		for (int i=0; i<sizeof(event_masks)/sizeof(EventMask); ++i) {
			if (event.mask & event_masks[i].flag) {
				printf("t%sn", event_masks[i].name);
			}
		}
	}
	return 0;
}

延伸阅读

评论