linux系统下监控文件更改–Inotify API使用方法
1.简介:
- 1.inotify机制可用于监控文件或目录。当监控目录时,与该目录自身以及该目录下面的文件都会被监控,其上有事件发生时都会通知给应用程序
- 2.otify监控机制为非递归,若应用程序有意监控整个目录子树内的事件,则需对该树中的每个目录发起inotify_add_watch()调用
- 可使用select(),poll(),epoll()以及由信号驱动的I/O来监控inotify文件描述符
ps:后面结合select()使用。
2.API
#include<sys/inotify.h>
int inotify_init(void);
该函数的返回值为一个文件描述符,我们可以简单的理解为该文件描述符所指代的文件中将会保存类似所监控的目录所发生的事件集
#include<sys/inotify.h>
int inotify_add_watch(int fd,const char *pathname,uint32_t mask);
针对fd所指的inotify实例的监控列表,系统调用inotify_add_watch()可以追加新的监控项。
参数pathname为想要创建的监控项所对应的文件,特别注意调用该接口必须要对该文件有读权限,该函数只对文件做一次检查,如果在监控时修改了所监控的文件读权限,则不会影响继续监控此文件
参数mask为一位掩码,针对pathname定义了想要监控的事件,此函数的返回值为一个用于唯一指代此监控项的描述符
3.事件
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不为目录时会失败
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指名发生事件的是哪个监控描述符,该字段值由之前对inotify_add_watch()的调用返回。因为用read()读到的是inotify文件中的所有事件,可是当inotify同时监控多个目录或文件时我们应该如何区分读到的内容呢,这是wd就派上了用场,我们可以用wd来做区分
.mask字段返回了描述该事件的位掩码
.cookie字段将相关事件联系到一起,目前只有对重命名时才会用到
.len字段表示实际分配给name字段的字节数
.name字段则是标示该文件.
ps:最好将监控的文档放入文件夹内,监控文件目录。如直接监控文件,文件内容如果更改(一般使用文本编辑器改变文件,编辑器首先删除文件,再创建。造成原来我们注册的文件描述符实效)则会返回IN_DELETE_SELF删除了受监控目录 造成无inotifyFd无效.
使用select接收更改事件:
源码:
#include<stdio.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/inotify.h>
#include<limits.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<sys/time.h>
#define BUF_LEN 1000
void display(struct inotify_event *i, char ** argv)
{
printf("wd = %2d;", i->wd);
if(i -> cookie > 0)
{
printf("cookie = %4d;",i->cookie);
}
printf("mask = ");
// if(i ->mask & IN_ACCESS) printf("IN_ACCESS\n");
// if(i ->mask & IN_DELETE_SELF) printf("IN_DELETE_SELF\n");
if(i ->mask & IN_MODIFY) printf("IN_MODIFY\n");
// if(i ->mask & IN_OPEN) printf("IN_OPEN\n");
if((i ->mask & IN_MODIFY)&&(i ->wd == 1)) //判断是那个文件进行了更改
{
printf("IN_ATTRIB %s Changed\n",argv[1]);
}else{
printf("IN_ATTRIB %s Changed\n",argv[2]);
}
if(i -> len > 0)
{
printf("name = %s\n",i->name);
}
}
int
main(int argc,char **argv)
{
int inotifyFd, wd, wd2, j;
char buf[BUF_LEN];
char *p;
struct inotify_event *event;
int flages;
ssize_t numRead;
fd_set inputs, testfds;
struct timeval timeout;
int result;
if(argc < 2)
{
printf("error\n");
}
inotifyFd = inotify_init(); //系统调用可创建一新的inotify实例 初始化实例
if(inotifyFd == -1)
{
printf("init error\n");
return 0;
}
wd = inotify_add_watch(inotifyFd,argv[1],IN_MODIFY); //系统调用inotify_add_watch()可以追加新的监控项
if(wd == -1)
{
printf("error wd\n");
}
wd2 = inotify_add_watch(inotifyFd,argv[2],IN_MODIFY);
if(wd2 == -1)
{
printf("error wd2\n");
}
printf("watching %s using wd = %d\n", argv[1],wd);
printf("watching %s using wd = %d\n", argv[2],wd2);
FD_ZERO(&inputs);
FD_SET(inotifyFd,&inputs); //将inotify的文件秒速符加入到 select中
while(1) //开启非阻塞select模式
{
int fd;
testfds = inputs;
result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0);
if (result < 1)
{
perror("server err");
}
for(fd = 0; fd < FD_SETSIZE; fd++)
{
if(FD_ISSET(fd, &testfds)) //收到消息 处理消息
{
if(fd == inotifyFd)
{
numRead = read(inotifyFd,buf,BUF_LEN);
if(numRead == -1)
{
printf("read error\n");
}
printf("read %ld bytes from inotify fd\n",(long)numRead);
for(p = buf; p < buf+numRead;)
{
event = (struct inotify_event *)p;
display(event,argv);
p+=sizeof(struct inotify_event)+ event->len;
}//for
}//if()
}//if(fd)
}//for()
}//while(1)
close(inotifyFd);
return 0;
}