有些情况下,我们难免需要监控一些文件的变化情况,这该如何实现呢?自然而然的我们会想要利用一个线程,每个一段时间便去看看文件的情况,这种方式本质上就是基于时间调度的轮训.虽然能够实现我们的需求,但是这种方式只适合文件经常变化的情况,其他情况下都非常低效,并且可能丢掉某些类型的变化,也就是说,这种方式无法实现实时的文件监控.
inotify简介
那还有其他的方式么?熟悉linux的童鞋应该记得从linux kernel 2.6.13开始引入inotify机制,用于通知用户相关文件变化情况:任何一个文件发生某种变化,都会产生一个相应的文件事件.
我们不仅好奇,文件的哪些事件能够被监控,也就是说inotify支持监控文件的哪些变化呢?继续往下看.
可监控事件类型
目前inotify能够监控的以下文件变化事件:
事件类型 | 说明 |
---|---|
IN_ACCESS | 文件被访问 |
IN_MODIFY | 文件被修改 |
IN_ATTRIB | 文件属性被修改 |
IN_CLOSE_WRITE | 可写文件被关闭 |
IN_CLOSE_NOWRITE | 不可写文件被关闭 |
IN_CLOSE | 文件被关闭,也就以上两者的集合 |
IN_OPEN | 文件被打开 |
IN_MOVED_FROM | 文件被移来 |
IN_MOVED_TO | 文件被移走 |
IN_MOVE | 文件被移动,也就是以上两者的集合 |
IN_CREATE | 文件被创建 |
IN_DELETE | 文件被删除 |
IN_DELETE_SELF | 自删除,也就是一个可执行文件在执行时尝试删除自己 |
IN_MOVE_SELF | 自移动,也就是一个可执行文件在执行时尝试移动自己 |
IN_UNMOUNT | 宿主文件系统被卸载 |
API说明
不难发现,inotify对文件监控的支持是非常全面的,足以满足我们绝大部门的需求.接下来,我们对innotify的api做个简单的说明:
方法 | 说明 |
---|---|
inotify_init | 用于创建一个inotify实例,返回一个指向该实例的的文件描述符 |
inotify_add_watch | 添加对文件或者目录的监控,可以指定需要监控那些文件变化事件 |
inotify_rm_watch | 从监控列表中移除监控文件或者目录 |
read | 读取事件信息 |
close | 关闭文件描述符,并会移除所有在该描述符上监控 |
事件通知
在可监控事件中,我们已经了解inotify支持的文件事件.现在来看看这些当这些文件事件产生时,其发出通知的结构.在inotify中,事件通知结构用结构体inotify_event表示:
struct inotify_event
{
int wd; //监控目标的watch描述符
uint32_t mask; //事件掩码
uint32_t cookie; //事件同步cookie
uint32_t len; //name字符串的长度
char name __flexarr; //被监视目标的路径名
};
这里需要记住一点:name字段并不是什么时候都有的:只有要监控的目标是一个目录,且产生的事件与目录内部的文件或子目录相关,且与目录本身无关时才会提供相应的name字段.
cookie用于关联被观察对象的IN_MOVED_FROM事件和IN_MOVED_TO 事件.
使用流程
了解以上之后,那么该怎么用呢?要实现对文件或者目录的监控需要经过以下几个步骤:
1. 创建inotify实例
在应用程序中,首先需要创建inotify实例:
int fd=inotify_init();
该方法创建了一个inotify实例,并返回一个文件描述符以便能够通过这个描述符访问到inotify实例.
2. 添加监控
在获得inotify实例产生的文件描述符之后,我们就可以为其添加watch.另外,我们也可以使用mask(事件掩码)来设置我们想监控的事件类型.当然可以我们也可以使用IN_ALL_EVENTS监控所有事件:
int wd=inotify_add_watch(fd,path,mask)
补充:
此处的fd即inotify_init()方法返回的文件描述符.每个文件描述符都有一个排序的事件序列.path则是需要监控的文件或者目录的路径.mask则是事件掩码,它表示应用程序对哪些事件感兴趣.
文件系统产生的事件由Watch对象来管理,该方法将返回的wd就是Watch对象的句柄.
3. 等待事件与循环处理
在为inotify实例添加watch之后,接下来就是等待事件了.为了能不断的处理事件,我们将其放在循环体当中.
在循环中,通过read()方法可以一次获得多个事件.在没有事件产生时,read()被阻塞,一旦有事件产生,那么我们就可以读取事件到的我们设置的事件数组中,然后对事件数组进行处理,其简单代码如下:
//事件数组,自定义设置,这里我们设置为128
char event_buf[128];
while(true){
int