inotify机制监控文件系统事件原理及使用

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

时间: 2024-10-01 03:26:19

inotify机制监控文件系统事件原理及使用的相关文章

Python Inotify 监视LINUX文件系统事件

Inotify 可以监视的LINUX文件系统事件包括: --IN_ACCESS,即文件被访问 --IN_MODIFY,文件被write --IN_ATTRIB,文件属性被修改,如chmod.chown.touch等 --IN_CLOSE_WRITE,可写文件被close --IN_CLOSE_NOWRITE,不可写文件被close --IN_OPEN,文件被open --IN_MOVED_FROM,文件被移走,如mv --IN_MOVED_TO,文件被移来,如mv.cp --IN_CREATE,

(转)Linux下通过rsync与inotify(异步文件系统事件监控机制)实现文件实时同步

Linux下通过rsync与inotify(异步文件系统事件监控机制)实现文件实时同步原文:http://www.summerspacestation.com/linux%E4%B8%8B%E9%80%9A%E8%BF%87rsync%E4%B8%8Einotify%E5%BC%82%E6%AD%A5%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E4%BA%8B%E4%BB%B6%E7%9B%91%E6%8E%A7%E6%9C%BA%E5%88%B6%E5%AE%9E

Inotify: 高效、实时的Linux文件系统事件监控框架

概要 - 为什么需要监控文件系统? 在日常工作中,人们往往需要知道在某些文件(夹)上都有那些变化,比如: 通知配置文件的改变 跟踪某些关键的系统文件的变化 监控某个分区磁盘的整体使用情况 系统崩溃时进行自动清理 自动触发备份进程 向服务器上传文件结束时发出通知 通常使用文件轮询的通知机制,但是这种机制只适用于经常改变的文件(因为它可以确保每过x秒就可以得到i/o),其他情况下都非常低效,并且有时候会丢失某些类型的变化,例如文件的修改时间没有改变.像Tripwire这样的数据完整性系统,它们基于时

从inotify机制说到FileObserver

有些情况下,我们难免需要监控一些文件的变化情况,这该如何实现呢?自然而然的我们会想要利用一个线程,每个一段时间便去看看文件的情况,这种方式本质上就是基于时间调度的轮训.虽然能够实现我们的需求,但是这种方式只适合文件经常变化的情况,其他情况下都非常低效,并且可能丢掉某些类型的变化,也就是说,这种方式无法实现实时的文件监控. inotify简介 那还有其他的方式么?熟悉linux的童鞋应该记得从linux 2.6.13之后,引入inotify机制,它是内核中用于通知文件变化的:任何一个文件发生某种变

CentOS 7 部署inotify实时监控(NFS服务器上部署,rsync服务器测试)

1.简介 1.1inotify   一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除.读.写和卸载操作等. 2.环境准备 [[email protected] ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [[email protected] ~]# uname -r 3.10.0-327.el7.x86_64 [[email protected] ~]# gete

Javascript引擎单线程机制及setTimeout执行原理说明

setTimeout用法在实际项目中还是会时常遇到.比如浏览器会聪明的等到一个函数堆栈结束后才改变DOM,如果再这个函数堆栈中把页面背景先从白色设为红色,再设回白色,那么浏览器会认为DOM没有发生任何改变而忽略这两句话,因此我们可以通过setTimeout把“设回白色”函数加入下一个堆栈,那么就可以确保背景颜色发生过改变了(虽然速度很快可能无法被察觉). 总之,setTimeout增加了Javascript函数调用的灵活性,为函数执行顺序的调度提供极大便利. 然后,我们从基础的层面来看看:理解J

[转]易语言消息机制分析(消息拦截原理)

标 题: [原创]易语言消息机制分析(消息拦截原理)作 者: 红绡枫叶时 间: 2014-12-17,12:41:44链 接: http://bbs.pediy.com/showthread.php?t=195626 我自己做了个易语言的sig签名,方便分析的时候用.易语言例子是静态编译的.版本 5.11易语言其实是基于mfc的,它依然需要mfc的消息派发机制,只不过,自己当了系统与用户间的代理人.所有的消息都要经它转发而已.我在MFC的消息派发函数_AfxDispatchCmdMsg下断点,总

Libevent的IO复用技术和定时事件原理

Libevent 是一个用C语言编写的.轻量级的开源高性能网络库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大:源代码相当精炼.易读:跨平台,支持 Windows. Linux. *BSD 和 Mac Os:支持多种 I/O 多路复用技术, epoll. poll. dev/poll. select 和 kqueue 等:支持 I/O,定时器和信号等事件:注册事件优先级. 1 Libevent中的epoll Libevent重

centos7.x rsync+inotify实时监控备份

#简介 rsync 官方描述 他是一个快速拷贝工具,可以复制到本地或者远程 Rsync is a fast and extraordinarily versatile file  copying  tool.   It can  copy  locally,  to/from  another  host  over  any remote shell, or to/from a remote rsync daemon.  It offers a  large  number  of  opti