1.基本描述
inotify提供了一种监控文件系统事件的机制,可以用来监控单个的文件以及目录。当一个目录被监控,inotify会返回该目录以及该目录下面文件的事件。
2.原理以及使用
2.1内核原理
inotify机制借用了内核里面的notify通知链技术,针对文件系统里面的使用主要是在inode结构体里面加入了相关的字段(内核版本3.10.0-327):
struct inode {
。。。
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */ 具体可能监控的事件,事件基本上是一些位段
struct hlist_head i_fsnotify_marks; /* 具体的链表,链表上可以挂入多个mask结构(事件) */
#endif
??。。。
}
struct fsnotify_mark {
__u32 mask; /* mask this mark is for */
atomic_t refcnt; /* active things looking at this mark */
struct fsnotify_group *group; /* group this mark is for */
struct list_head g_list; /* list of marks by group->i_fsnotify_marks */
spinlock_t lock; /* protect group and inode */
union {
struct fsnotify_inode_mark i;
struct fsnotify_vfsmount_mark m;
};
__u32 ignored_mask; /* events types to ignore */
unsigned int flags; /* vfsmount or inode mark? */
struct list_head destroy_list;
void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
};
具体的关系结构图如图所示:
图1 inotify机制内核下面各结构体关系图
2.2用户态接口原理
用户态接口的基本思路是初始化一个具体的inotify实例,并设置实例以及具体监控哪些事件。当有具体的事件以后可以读取对应的
结构体来解析。事件结构体为:
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 */ 具体的文件名
};
具体可以监控的事件主要有:(注释比较清晰了)
IN_ACCESS File was accessed (read) (*).
IN_ATTRIB Metadata changed, e.g., permissions, timestamps, extended attributes, link count (since Linux 2.6.25), UID, GID, etc. (*).
IN_CLOSE_WRITE File opened for writing was closed (*).
IN_CLOSE_NOWRITE File not opened for writing was closed (*).
IN_CREATE File/directory created in watched directory (*).
IN_DELETE File/directory deleted from watched directory (*).
IN_DELETE_SELF Watched file/directory was itself deleted.
IN_MODIFY File was modified (*).
IN_MOVE_SELF Watched file/directory was itself moved.
IN_MOVED_FROM Generated for the directory containing the old filename when a file is renamed (*).
IN_MOVED_TO Generated for the directory containing the new filename when a file is renamed (*).
IN_OPEN File was opened (*).
主要的用户态接口函数:
int inotify_init(void);
int inotify_init1(int flags);
初始化一个inotify实例并且返回一个文件描述符,inotify实例和一个evnet队列挂钩,失败返回-1。
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
添加一个watch对象,fd为具体的inotify实例描述符,pathname为监控的目录或者文件,mask为具体的事件,成功返回非负整数,失败返
回-1.
int inotify_rm_watch(int fd, int wd);
删除一个watch,fd为inotify实例描述符,wd为watch描述符。成功返回0,失败返回-1.
3.测试程序
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.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 giNotifyFd;
int giaWds[20];
int giCount;
int watch_inotify_events(int fd)
{
char event_buf[512];
int ret;
int event_pos = 0;
int event_size = 0;
struct inotify_event *event;
time_t tNow;
struct tm *pTimeNow;
/* 读事件是否发生,没有发生就会阻塞 */
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) {
// time(&tNow); /* 这三行可以注释掉,之前出现过加了这三行执行出现core dump的问题。 */
// pTimeNow = localtime(&tNow);
// printf("Local time is:%s", asctime(pTimeNow));
if(event->mask & IN_CREATE){
printf("watch is %d, create file: %s\n", event->wd, event->name);
} else {
printf("watch is %d, delete file: %s\n", event->wd, event->name);
}
if (event->mask & IN_ATTRIB) {
printf("watch is %d, modify file attribute: %s\n", event->wd, event->name);
}
}
/* event_size就是一个事件的真正大小 */
event_size = sizeof(struct inotify_event) + event->len;
ret -= event_size;
event_pos += event_size;
}
return 0;
}
/* 递归处理目录 */
void init_all_iwds(char *pcName)
{
int iWd;
struct stat tStat;
DIR *pDir;
struct dirent *ptDirent;
char caNametmp[100]; // 存储目录名字
iWd = inotify_add_watch(giNotifyFd, pcName, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
giaWds[giCount] = iWd;
giCount++;
if (-1 == stat(pcName, &tStat)) {
printf("stat %s error\n", pcName);
return;
}
if (!S_ISDIR(tStat.st_mode))
return;
/* now the child dir */
pDir = opendir(pcName);
if (NULL == pDir) {
printf("opendir %s error\n", pcName);
return;
}
// 循环读目录下面的子项
while (NULL != (ptDirent = readdir(pDir))) {
if ((0 == strcmp(ptDirent->d_name, ".")) || (0 == strcmp(ptDirent->d_name, "..")))
continue; // 跳过当前目录和上一级父目录
// printf("sub name is %s, d_type is 0x%x\n", ptDirent->d_name, ptDirent->d_type);
sprintf(caNametmp, "%s/%s", pcName, ptDirent->d_name); //获取子目录或文件名字
if (-1 == stat(caNametmp, &tStat)) {
printf("stat error:%s\n", caNametmp); // 获取统计数据
return;
}
if (!S_ISDIR(tStat.st_mode)) //看是否是子目录,原则只处理目录
continue;
printf("sub absoulte dir name is %s\n", caNametmp);
// iWd = inotify_add_watch(giNotifyFd, caNametmp, IN_CREATE|IN_DELETE|IN_ATTRIB|IN_MODIFY);
init_all_iwds(caNametmp); //处理子目录
}
// 关闭
closedir(pDir);
}
int main(int argc, char** argv)
{
int iNotifyRet;
fd_set fds;
int iaWd[10];
int icount = 0;
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对象 */
init_all_iwds(argv[1]);
/* 处理事件 */
while (1) {
FD_ZERO(&fds);
FD_SET(iNotifyFd, &fds);
if (select(iNotifyFd+1, &fds, NULL, NULL, NULL) > 0) {
iNotifyRet = watch_inotify_events(iNotifyFd);
if (-1 == iNotifyRet)
break;
}
}
/* 删除inotify的watch对象 */
for (icount = 0; icount < giCount; icount++) {
if (inotify_rm_watch(iNotifyFd, giaWds[icount ]) == -1) {
printf("notify_rm_watch %d error!\n", giaWds[icount]);
return -1;
}
}
/* 关闭inotify描述符 */
close(iNotifyFd);
return 0;
}
文件命名为:inotify.c
编译: gcc -o inotify inotify.c 生成可执行文件
执行: ./inotify输出 Usage: ./inotify <dir> 提示需要输入具体监控的目录或者文件。
执行: ./inotify /home/work/0604_inotify/ &
创建 aaa 文件,打印出 watch is 1, create file: aaa
修改aaa文件属性,打印出 watch is 1, modify file attribute: aaa
原文地址:https://www.cnblogs.com/samdyhc/p/9300270.html