在日常的运维过程中,经常需要备份某些文件,或者对系统的某些文件进行监控,比如重要的配置文件等。如果需要作到实时同步或者监控,就需要使用内核的inotify机制
Inotify 是基于inode级别的文件系统监控技术,是一种强大的、细粒度的、异步的机制,它满足各种各样的文件监控需要,不仅限于安全和性能
注:上面所说的文件也包括目录
[root@RHEL5 Rsync]# 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
注意: max_queued_events 是 Inotify 管理的队列的最大长度,文件系统变化越频繁,这个值就应该越大
如果你在日志中看到Event Queue Overflow,说明max_queued_events太小需要调整参数后再次使用.
[root@localhost ]# cat /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
[root@localhost]# /tmp/test.sh DELETE-/home/admin/cronolog-1.6.2.bak/COPYING DELETE-/home/admin/cronolog-1.6.2.bak/doc/cronolog.info DELETE-/home/admin/cronolog-1.6.2.bak/doc/cronolog.texi DELETE-/home/admin/cronolog-1.6.2.bak/doc/Makefile.am DELETE-/home/admin/cronolog-1.6.2.bak/doc/Makefile.in DELETE-/home/admin/cronolog-1.6.2.bak/doc/texinfo.tex DELETE-/home/admin/cronolog-1.6.2.bak/doc/cronosplit.1m DELETE-/home/admin/cronolog-1.6.2.bak/doc/Makefile DELETE-/home/admin/cronolog-1.6.2.bak/doc/cronolog.1m DELETE,ISDIR-/home/admin/cronolog-1.6.2.bak/doc DELETE-/home/admin/cronolog-1.6.2.bak/TODO DELETE-/home/admin/cronolog-1.6.2.bak/src/cronotest.c DELETE-/home/admin/cronolog-1.6.2.bak/src/cronolog.c DELETE-/home/admin/cronolog-1.6.2.bak/src/cronoutils.h DELETE-/home/admin/cronolog-1.6.2.bak/src/cronoutils.c DELETE-/home/admin/cronolog-1.6.2.bak/src/Makefile.am DELETE-/home/admin/cronolog-1.6.2.bak/src/Makefile.in DELETE-/home/admin/cronolog-1.6.2.bak/src/cronosplit.in DELETE-/home/admin/cronolog-1.6.2.bak/src/Makefile DELETE-/home/admin/cronolog-1.6.2.bak/src/cronosplit DELETE-/home/admin/cronolog-1.6.2.bak/src/config.h DELETE,ISDIR-/home/admin/cronolog-1.6.2.bak/src DELETE-/home/admin/cronolog-1.6.2.bak/lib/getopt1.c DELETE-/home/admin/cronolog-1.6.2.bak/lib/getopt.h DELETE-/home/admin/cronolog-1.6.2.bak/lib/Makefile.am DELETE-/home/admin/cronolog-1.6.2.bak/lib/Makefile.in DELETE-/home/admin/cronolog-1.6.2.bak/lib/localtime_r.c DELETE-/home/admin/cronolog-1.6.2.bak/lib/getopt.c DELETE-/home/admin/cronolog-1.6.2.bak/lib/Makefile DELETE-/home/admin/cronolog-1.6.2.bak/lib/strptime.c DELETE,ISDIR-/home/admin/cronolog-1.6.2.bak/lib DELETE-/home/admin/cronolog-1.6.2.bak/config.cache DELETE-/home/admin/cronolog-1.6.2.bak/install-sh DELETE-/home/admin/cronolog-1.6.2.bak/Makefile.am DELETE-/home/admin/cronolog-1.6.2.bak/README DELETE-/home/admin/cronolog-1.6.2.bak/AUTHORS DELETE-/home/admin/cronolog-1.6.2.bak/Makefile.in DELETE-/home/admin/cronolog-1.6.2.bak/testsuite/Makefile.am DELETE-/home/admin/cronolog-1.6.2.bak/testsuite/README DELETE-/home/admin/cronolog-1.6.2.bak/testsuite/Makefile.in DELETE-/home/admin/cronolog-1.6.2.bak/testsuite/Makefile DELETE,ISDIR-/home/admin/cronolog-1.6.2.bak/testsuite DELETE-/home/admin/cronolog-1.6.2.bak/cronolog.spec DELETE-/home/admin/cronolog-1.6.2.bak/NEWS DELETE-/home/admin/cronolog-1.6.2.bak/configure DELETE-/home/admin/cronolog-1.6.2.bak/ChangeLog DELETE-/home/admin/cronolog-1.6.2.bak/missing DELETE-/home/admin/cronolog-1.6.2.bak/config.log DELETE-/home/admin/cronolog-1.6.2.bak/aclocal.m4 DELETE-/home/admin/cronolog-1.6.2.bak/Makefile DELETE-/home/admin/cronolog-1.6.2.bak/INSTALL DELETE-/home/admin/cronolog-1.6.2.bak/config.status DELETE-/home/admin/cronolog-1.6.2.bak/configure.in DELETE-/home/admin/cronolog-1.6.2.bak/mkinstalldirs DELETE,ISDIR-/home/admin/cronolog-1.6.2.bak详细请参考 man inotify , man inotifywait
Incron是inotify的cron系统,与os本身的cron一样,包含一个后台守护进程(incrond)和一个事件编辑器(incrontab
与os本身的cron不同的仅仅是触发时间的是os对某个文件(夹)的操作而不是时间,由系统事件触发的机制,对于应用系统来说,几乎可以做到实时性。
[root@localhost]# yum install Incron
选项说明:
Mar 23 14:05:19 localhost incrond[6857]: (root) CMD (echo "/home/admin = = IN_OPEN,IN_ISDIR") Mar 23 14:05:19 localhost incrond[6857]: (root) CMD (echo "/home/admin = = IN_CLOSE_NOWRITE,IN_ISDIR") Mar 23 14:05:20 localhost incrond[6857]: (root) CMD (echo "/home/admin = = IN_OPEN,IN_ISDIR") Mar 23 14:05:20 localhost incrond[6857]: (root) CMD (echo "/home/admin = = IN_CLOSE_NOWRITE,IN_ISDIR") Mar 23 14:05:20 localhost incrond[6857]: (root) CMD (echo "/home/admin = ssss = IN_DELETE")
总体来说,在文件和目录实时监控还是很有效的,可以结合其他工具来作统一化的解决方案,比如使用syslog-ng作统一化收集,当然最重要还是要有场景.
使用inotify API有以下关键步骤:
使用inotify_init()创建一个inotify实例,返回的文件描述符用于后续操作中指代该实例。
使用inotify_add_watch向inotify实例的监控列表添加条目,即添加应用感兴趣的文件或目录。每个监控项包含一个路径名及相关的位掩码,位掩码指明所要监控的事件。inotify_add_watch将返回一个监控描述符,用于后续操作指代该监控项(inotify_rm_watch移除监控项)。
应用需要对inotify文件描述符执行read()操作以获取事件通知,read()调用会返回一个或多个inotify_event结构,记录了所发生的事件。
应用结束时关闭inotify描述符,这会自动清除与inotify实例相关的所有监控项。
inotify可用于监控文件或目录。监控目录时,与路径自身及其所含文件相关的事件都会通知应用程序。
inofity为非递归的,若想监控目录下整个目录树,需要对目录树下每个目录发起inotify_add_watch调用。
#include
int inotify_init(void);
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
int inotify_rm _watch(int fd, uint32_t wd);
使用inotify_add_watch添加、修改监控项时,位掩码参数mask标识了针对 给定路径名要监控的事件。下表中“in”列列出了可以mask中定义的事件位。
在监控列表中添加监控项后,应用程序可用read从inotify文件描述符中读取事件,以判定发生了哪些事件。若读取时还没有事件发生,read将阻塞直到事件发生(除非设置O_NONBLOCK标志)。
事件发生后,每次调用read会返回一个缓冲区,包含一个或多个如下类型的结构:
struct inotify_event {
int wd; //Watch descriptor on which event occurred
uint32_t mask; //Bits describing event that occurred
uint32_t cookie; //Cookie for related events(for rename())
uint32_t len; //Size of 'name' field
char name[]; //Optional null-terminated filename
}
#include
#include
#include "tlpi_hdr.h"
static void /* Display information from inotify_event structure */
displayInotifyEvent(struct inotify_event *i)
{
printf(" wd =%2d; ", i->wd);
if (i->cookie > 0)
printf("cookie =%4d; ", i->cookie);
printf("mask = ");
if (i->mask & IN_ACCESS) printf("IN_ACCESS ");
if (i->mask & IN_ATTRIB) printf("IN_ATTRIB ");
if (i->mask & IN_CLOSE_NOWRITE) printf("IN_CLOSE_NOWRITE ");
if (i->mask & IN_CLOSE_WRITE) printf("IN_CLOSE_WRITE ");
if (i->mask & IN_CREATE) printf("IN_CREATE ");
if (i->mask & IN_DELETE) printf("IN_DELETE ");
if (i->mask & IN_DELETE_SELF) printf("IN_DELETE_SELF ");
if (i->mask & IN_IGNORED) printf("IN_IGNORED ");
if (i->mask & IN_ISDIR) printf("IN_ISDIR ");
if (i->mask & IN_MODIFY) printf("IN_MODIFY ");
if (i->mask & IN_MOVE_SELF) printf("IN_MOVE_SELF ");
if (i->mask & IN_MOVED_FROM) printf("IN_MOVED_FROM ");
if (i->mask & IN_MOVED_TO) printf("IN_MOVED_TO ");
if (i->mask & IN_OPEN) printf("IN_OPEN ");
if (i->mask & IN_Q_OVERFLOW) printf("IN_Q_OVERFLOW ");
if (i->mask & IN_UNMOUNT) printf("IN_UNMOUNT ");
printf("\n");
if (i->len > 0)
printf(" name = %s\n", i->name);
}
#define BUF_LEN (10 * (sizeof(struct inotify_event) + NAME_MAX + 1))
int
main(int argc, char *argv[])
{
int inotifyFd, wd, j;
char buf[BUF_LEN] __attribute__ ((aligned(8)));
ssize_t numRead;
char *p;
struct inotify_event *event;
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageErr("%s pathname...\n", argv[0]);
inotifyFd = inotify_init(); /* Create inotify instance */
if (inotifyFd == -1)
errExit("inotify_init");
/* For each command-line argument, add a watch for all events */
for (j = 1; j < argc; j++) {
wd = inotify_add_watch(inotifyFd, argv[j], IN_ALL_EVENTS);
if (wd == -1)
errExit("inotify_add_watch");
printf("Watching %s using wd %d\n", argv[j], wd);
}
for (;;) { /* Read events forever */
numRead = read(inotifyFd, buf, BUF_LEN);
if (numRead == 0)
fatal("read() from inotify fd returned 0!");
if (numRead == -1)
errExit("read");
printf("Read %ld bytes from inotify fd\n", (long) numRead);
/* Process all of the events in buffer returned by read() */
for (p = buf; p < buf + numRead; ) {
event = (struct inotify_event *) p;
displayInotifyEvent(event);
p += sizeof(struct inotify_event) + event->len; //event->len 变动的文件名长度。
}
}
exit(EXIT_SUCCESS);
}