转载自:http://blog.csdn.net/longwang155069/article/details/54016789
http://blog.csdn.net/shreck66/article/details/46974155
Inotify
是一个Linux
特性,它监控文件系统操作,比如读取、写入和创建。Inotify
反应灵敏,用法非常简单,并且比cron
任务的繁忙轮询高效得多。学习如何将inotify
集成到您的应用程序中,并发现一组可用来进一步自动化系统治理的命令行工具。
系统治理就像日常生活一样。就像刷牙和吃蔬菜一样,日常的维护能保持机器的良好状态。您必须定期清空废物,比如临时文件或无用的日志文件,以及花时间填写表单、回复电话、更新和监控进程等。幸好自动化shell
脚本、使用Nagios
等工具进行监控、通过常见的cron
进行任务调度可以减轻这个负担。
但稀奇的是,这些工具没有一个具有响应性。当然,您可以安排一个频繁运行的 cron
任务来监控条件,但这样繁忙的轮询 — 消耗大量资源并且具有不确定性 — 并不是很理想。例如,假如您必须监控输入数据的几个
Transfer Protocol(FTP)
收存箱,您可能要通过find
命令扫描每个目标目录,列举新的内容。然而,尽管这个操作看起来并没有什么害处,但每个调用都产生一个新的 shell
和 find
命令,这需要许多系统调用来打开目录,然后扫描目录,等等。这会造成过于频繁的或大量的轮询任务(更糟糕的是,繁忙的轮询并不总是很好。想象一下一个文件系统浏览器,比如 Mac OS X
的 Finder
,轮询更新时需要的大量资源及其复杂性)。
Inotify
是一个Linux
内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。
使用 inotify
很简单:创建一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),然后使用read
方法从描述符获取事件。read
并不会用光整个周期,它在事件发生之前是被阻塞的。
更好的是,因为inotify
通过传统的文件描述符工作,您可以利用传统的 select
系统调用来被动地监控监视器和许多其他输入源。两种方法 — 阻塞文件描述符和使用 select
— 都避免了繁忙轮询。
1.概述
1)inotify
机制可用于监控文件或目录。当监控目录时,与该目录自身以及该目录下面的文件都会被监控,其上有事件发生时都会通知给应用程序
2)inotify
监控机制为非递归,若应用程序有意监控整个目录子树内的事件,则需对该树中的每个目录发起inotify_add_watch()调用
3)可使用select(),poll(),epoll()
以及由信号驱动的I/O来监控inotify文件描述符
2.inotify API
1)inotify_init()
系统调用可创建一新的inotify实例
#include<sys/inotify.h>
int inotify_init(void);
该函数的返回值为一个文件描述符,我们可以简单的理解为该文件描述符所指代的文件中将会保存类似所监控的目录所发生的事件集
2)针对fd
所指的inotify
实例的监控列表,系统调用inotify_add_watch()
可以追加新的监控项
#include<sys/inotify.h>
int inotify_add_watch(int fd,const char *pathname,uint32_t mask);
参数pathname
为想要创建的监控项所对应的文件,特别注意调用该接口必须要对该文件有读权限,该函数只对文件做一次检查,如果在监控时修改了所监控的文件读权限,则不会影响继续监控此文件
参数mask
为一位掩码,针对pathname
定义了想要监控的事件,此函数的返回值为一个用于唯一指代此监控项的描述符
3)inotify_rm_watch(int fd, int wd)
用于从watch list
中移除检测的对象。
3.inotify事件
IN_ACCESS 文件被访问
IN_ATTRIB 文件元数据改变
IN_CLOSE_WRITE 关闭为了写入而打开的文件
IN_CREATE 在受监控目录下创建了文件或目录
IN_DELETE 在受监控目录内删除了文件或目录
IN_DELETE_SELF 删除了受监控目录/文件本身
IN_MODIFY 文件被修改
IN_MODIFY_SELF 移动受监控目录或文件本身
IN_MOVED_FROM 文件移除受监控目录
IN_MOVED_TO 将文件移到受监控目录
IN_OPEN 文件被打开
IN_ALL_EVENTS 以上所有输出事件的统称
IN_MOVE IN_MOVED_FROM | IN_MOVED_TO事件的统称
IN_ONESHOT 只监控pathname的一个事件
IN_ONLYDIR pathname不为目录时会失败
1)当文件的元数据(比如,权限,所有权,链接计数,扩展属性,用户ID,或组ID等)改变时,会发生IN_ATTRIB
事件
2)删除受监控对象时会发生IN_DELETE_SELF
3)重命名对象时会发生IN_MORE_SELF
事件
4)ONE_SHOT
允许只监控pathname
的一个事件,事件发生后,监控项会自动从监控列表消失
4.读取inotify
事件
将监控项项在监控列表中登记后,应运程序可用read()
从inotify
的文件描述符中读取事件,以判定发生了那些事件。若读取之时还没有发生任何事件,则read()
会阻塞,直至有事件产生,事件发生后,每次调用read()会返回一个缓存区,内含一个或多个如下类型的结构体
struct inotify_event {
int wd; /* Watch descriptor */
uint32_t mask; /* Mask of events */
uint32_t cookie; /* Unique cookie associating related
events (for rename(2)) */
uint32_t len; /* Size of name field */
char name[]; /* Optional null-terminated name */
};
.wd
: 就是检测的对象的watch descriptor
.mask
: 检测事件的mask
.cookie
: 和rename
事件相关。
.len
: name
字段的长度。
.name
: 检测对象的name
。
实例:
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
/*
struct inotify_event {
int wd; // Watch descriptor
uint32_t mask; // Mask of events
uint32_t cookie; // Unique cookie associating related events (for rename(2))
uint32_t len; // Size of name field
char name[]; // Optional null-terminated name
};
*/
int watch_inotify_events(int fd)
{
char event_buf[512];
int ret;
int event_pos = 0;
int event_size = 0;
struct inotify_event *event;
/*读事件是否发生,没有发生就会阻塞*/
ret = read(fd, event_buf, sizeof(event_buf));
/*如果read的返回值,小于inotify_event大小出现错误*/
if(ret < (int)sizeof(struct inotify_event))
{
printf("counld not get event!\n");
return -1;
}
/*因为read的返回值存在一个或者多个inotify_event对象,需要一个一个取出来处理*/
while( ret >= (int)sizeof(struct inotify_event) )
{
event = (struct inotify_event*)(event_buf + event_pos);
if(event->len)
{
if(event->mask & IN_CREATE)
{
printf("create file: %s\n",event->name);
}
else
{
printf("delete file: %s\n",event->name);
}
}
/*event_size就是一个事件的真正大小*/
event_size = sizeof(struct inotify_event) + event->len;
ret -= event_size;
event_pos += event_size;
}
return 0;
}
int main(int argc, char** argv)
{
int InotifyFd;
int ret;
if (argc != 2)
{
printf("Usage: %s <dir>\n", argv[0]);
return -1;
}
/*inotify初始化*/
InotifyFd = inotify_init();
if( InotifyFd == -1)
{
printf("inotify_init error!\n");
return -1;
}
/*添加watch对象*/
ret = inotify_add_watch(InotifyFd, argv[1], IN_CREATE | IN_DELETE);
/*处理事件*/
watch_inotify_events(InotifyFd);
/*删除inotify的watch对象*/
if ( inotify_rm_watch(InotifyFd, ret) == -1)
{
printf("notify_rm_watch error!\n");
return -1;
}
/*关闭inotify描述符*/
close(InotifyFd);
return 0;
}