当前位置: 首页 > 工具软件 > inotify-sync > 使用案例 >

inotify

宰父桐
2023-12-01
 
 
 

2010-12-20 10:00:29|  分类:Linux系统编程|  标签:|字号 订阅

inotify是什么?用它能干些什么?

         通俗点说它是一个内核用于通知用户空间程序文件系统变化的系统,并且它是powerful yet simple的。

inotify是linux系统2.6.13以后内核支持的一种特性,功能是监视文件系统的变化,在监听到文件系统变化后会向相应的应用程序发送事件。典型应用场景比如文件管理器,理想情况下应该在用户使用其他手段修改了文件夹的内容后马上反映出最新的内容,而不应该在用户手动刷新后才显示最新内容。如果没有类似inotify的机制,一般会采用轮询的方式实现这种功能,不能在第一时间反映文件系统变化而且浪费CPU时间。

 

 

inotify的用户接口原型主要有以下3个:
#include  <sys/inotify.h>

初始化:int inotify_init(void);

               int    fd  =  inotify_init();
添加监视对象:int inotify_add_watch(int fd, const char *path, uint32_t mask);

             int   wd   = inotify_add_watch(fd,path,mask);

             fd是inotify_init()的返回值。

             const  char *path是要监控的文件(目录)的路径。

             uint32_t   mask是:

             IN_ACCESS,文件被访问
  • IN_ATTRIB,文件属性被修改
  • IN_CLOSE_WRITE,可写文件被关闭
  • IN_CLOSE_NOWRITE,不可写文件被关闭
  • IN_CREATE,文件/文件夹被创建
  • IN_DELETE,文件/文件夹被删除
  • IN_DELETE_SELF,被监控的对象本身被删除
  • IN_MODIFY,文件被修改
  • IN_MOVE_SELF,被监控的对象本身被移动
  • IN_MOVED_FROM,文件被移出被监控目录
  • IN_MOVED_TO,文件被移入被监控目录
  • IN_OPEN,文件被打开

             还有非常多的事件可以使用。使用man   inotify可以查看所有可以监听的事件。

             mask是上面这些事件的或。例如IN_ACCESS|IN_MODIFY。

             返回值:wd表示对那个文件进行监控。
删除监视对象:int inotify_rm_watch(int fd, uint32_t wd);

             参数fd是inotify_init的返回值。

                    wd是inotify_add_watch的返回值。

             inotify_rm_watch删除对wd所指向的文件的监控。

读取监控发生的事件:

             size_t len = read(fd, buf, BUF_LEN);

             读取事件数据,buf应是一个指向inotify_event结构数组的指针。不过要注意的是inotify_event的name成员长度是可变的,这个问题后面再解释。

             注意:其中buf是一个指向struct  inotify_event数组的指针。

                      由于struct   inotify_event长度是可变的,因此在读取inotify_event数组内容的时候需要动态计算一下时间数据的偏移量。index += sizeof(struct inotify_event)+event->len,len即name成员的长度。

        

其实还是没有讲的很清楚,不过看了下面的例子,一定非常清楚:

#include <stdio.h>   
#include <unistd.h>   
#include <sys/select.h>   
#include <errno.h>   
#include <sys/inotify.h>   
  
static void   _inotify_event_handler(struct inotify_event *event)      //从buf中取出一个事件。  
{   
         printf("event->mask: 0x%08x\n", event->mask);   
         printf("event->name: %s\n", event->name);   
}   
  
int  main(int argc, char **argv)   
{   
  if (argc != 2) {   
    printf("Usage: %s <file/dir>\n", argv[0]);   
    return -1;   
  }   
  
  unsigned char buf[1024] = {0};   
  struct inotify_event *event = NULL;              


  int fd = inotify_init();                 //初始化监视器
  int wd = inotify_add_watch(fd, argv[1], IN_ALL_EVENTS);                  //监控指定文件的ALL_EVENTS。
  
  for (;;) 

  {   
       fd_set fds;   
       FD_ZERO(&fds);                
       FD_SET(fd, &fds);   


       if (select(fd + 1, &fds, NULL, NULL, NULL) > 0)                //监控fd的事件。当有事件发生时,返回值>0

       {   
           int len, index = 0;   
           while (((len = read(fd, &buf, sizeof(buf))) < 0) && (errno == EINTR));      //没有读取到事件。
           while (index < len) 

           {   
                  event = (struct inotify_event *)(buf + index);                      
                  _inotify_event_handler(event);                                             //获取事件。
                  index += sizeof(struct inotify_event) + event->len;             //移动index指向下一个事件
           }   
       }   
  }   
  
  inotify_rm_watch(fd, wd);              //删除对指定文件的监控。
  
  return 0;   
}  
22222222222-------------------------------------------------------------------------------------------------------------------------------

 江淼的Blog

    Linux下使用inotify监控文件动作
在日常应用中,常常会遇到以下场景,监控文件夹A,若文件夹中的B文件发生变化,则执行C命令。Linux下可以通过inotify完成该功能。
自从Linux kernel 2.6.13起,inotify以作为内核的一部份,同时需要glibc 2.4以上版本。

1. 相关函数

inotify_init() - 创建一个inotify实例
inotify_add_watch(int fd, const char *pathname, uint32_t mask) - 加入文件或目录到inotify进行监测
inotify_rm_watch(int fd, int wd) - 移除一个watcher

2. 相关结构

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 */
           };

3. Mask

适用于 inotify_add_watch mask 与 read 返回的inotify_event中mask
IN_ACCESS文件被访问
IN_ATTRIB文件属性发生变化
IN_CLOSE_WRITE以write方式打开文件并关闭
IN_CLOSE_NOWRITE以非write方式打开文件并关闭
IN_CREATE文件或目录被创建
IN_DELETE文件或目录被删除(被监测的文件夹A中B文件被删除)
IN_DELETE_SELF被监测的文件或目录被删除(被监测的文件夹A被删除)
IN_MODIFY文件被修改
IN_MOVE_SELF被监测的文件或目录移动
IN_MOVED_FROM文件移出被监测的目录
IN_MOVED_TO文件移入被监测的目录
IN_OPEN文件被打开
上述flag的集合
IN_ALL_EVENTS以上所有flag的集合
IN_MOVEIN_MOVED_TO|IN_MOVED_FROM
IN_CLOSEIN_CLOSE_WRITE|IN_CLOSE_NOWRITE
不常用的flag
IN_DONT_FOLLOW不follow符号链接 (since 2.6.15)
IN_EXCL_UNLINK当文件从监测目中unlink后,则不再报告该文件的相关event,比如监控/tmp使用 (since 2.6.36)
IN_MASK_ADD追打MASK到被监测的pathname
IN_ONESHOT只监测一次
IN_ONLYDIR只监测目录
仅由read返回
IN_IGNOREDinotify_rm_watch,文件被删除或者文件系统被umount
IN_ISDIR发生事件的是一个目录
IN_Q_OVERFLOWEvent队列溢出
IN_UNMOUNT文件系统unmount

4. 例子

用途:监测指定文件或目录(或不指定则为当前目录)的一切动作。
使用:inotify [文件或目录]
  1. #include <unistd.h>   
  2. #include <sys/inotify.h>   
  3. #include <stdio.h>   
  4. #include <error.h>   
  5. #include <errno.h>   
  6. #include <string.h>   
  7.   
  8. #define ERROR(text) error(1, errno, "%s", text)  
  9.   
  10. struct EventMask {   
  11.     int        flag;   
  12.     const char *name;   
  13.   
  14. };   
  15.   
  16. int freadsome(void *dest, size_t remain, FILE *file)   
  17. {   
  18.     char *offset = (char*)dest;   
  19.     while (remain) {   
  20.         int n = fread(offset, 1, remain, file);   
  21.         if (n==0) {   
  22.             return -1;   
  23.         }   
  24.   
  25.         remain -= n;   
  26.         offset += n;   
  27.     }   
  28.     return 0;   
  29. }   
  30.   
  31. int main(int argc, char *argv[])   
  32. {   
  33.     const char *target;   
  34.     if (argc == 1) {   
  35.         target = ".";   
  36.     } else {   
  37.         target = argv[1];   
  38.     }   
  39.   
  40.     EventMask event_masks[] = {   
  41.            {IN_ACCESS        , "IN_ACCESS"}        ,     
  42.            {IN_ATTRIB        , "IN_ATTRIB"}        ,     
  43.            {IN_CLOSE_WRITE   , "IN_CLOSE_WRITE"}   ,     
  44.            {IN_CLOSE_NOWRITE , "IN_CLOSE_NOWRITE"} ,     
  45.            {IN_CREATE        , "IN_CREATE"}        ,     
  46.            {IN_DELETE        , "IN_DELETE"}        ,     
  47.            {IN_DELETE_SELF   , "IN_DELETE_SELF"}   ,     
  48.            {IN_MODIFY        , "IN_MODIFY"}        ,     
  49.            {IN_MOVE_SELF     , "IN_MOVE_SELF"}     ,     
  50.            {IN_MOVED_FROM    , "IN_MOVED_FROM"}    ,     
  51.            {IN_MOVED_TO      , "IN_MOVED_TO"}      ,     
  52.            {IN_OPEN          , "IN_OPEN"}          ,     
  53.   
  54.            {IN_DONT_FOLLOW   , "IN_DONT_FOLLOW"}   ,     
  55.            {IN_EXCL_UNLINK   , "IN_EXCL_UNLINK"}   ,     
  56.            {IN_MASK_ADD      , "IN_MASK_ADD"}      ,     
  57.            {IN_ONESHOT       , "IN_ONESHOT"}       ,     
  58.            {IN_ONLYDIR       , "IN_ONLYDIR"}       ,     
  59.   
  60.            {IN_IGNORED       , "IN_IGNORED"}       ,     
  61.            {IN_ISDIR         , "IN_ISDIR"}         ,     
  62.            {IN_Q_OVERFLOW    , "IN_Q_OVERFLOW"}    ,     
  63.            {IN_UNMOUNT       , "IN_UNMOUNT"}       ,     
  64.     };   
  65.   
  66.     int monitor = inotify_init();   
  67.     if ( -1 == monitor ) {   
  68.         ERROR("monitor");   
  69.     }   
  70.   
  71.     int watcher = inotify_add_watch(monitor, target, IN_ALL_EVENTS);   
  72.     if ( -1 == watcher  ) {   
  73.         ERROR("inotify_add_watch");   
  74.     }   
  75.   
  76.     FILE *monitor_file = fdopen(monitor, "r");   
  77.     char last_name[1024];   
  78.     char name[1024];   
  79.   
  80.     /* event:inotify_event -> name:char[event.len] */  
  81.     while (true) {   
  82.         inotify_event event;   
  83.         if ( -1 == freadsome(&event, sizeof(event), monitor_file) ) {   
  84.             ERROR("freadsome");   
  85.         }   
  86.         if (event.len) {   
  87.             freadsome(name, event.len, monitor_file);   
  88.         } else {   
  89.             sprintf(name, "FD: %d\n", event.wd);   
  90.         }   
  91.   
  92.         if (strcmp(name, last_name) != 0) {   
  93.             puts(name);   
  94.             strcpy(last_name, name);   
  95.         }   
  96.   
  97.         /* 显示event的mask的含义 */  
  98.         for (int i=0; i<sizeof(event_masks)/sizeof(EventMask); ++i) {   
  99.             if (event.mask & event_masks[i].flag) {   
  100.                 printf("\t%s\n", event_masks[i].name);   
  101.             }   
  102.         }   
  103.     }   
  104.     return 0;   
  105. }  
#include <unistd.h>
#include <sys/inotify.h>
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <string.h>

#define ERROR(text) error(1, errno, "%s", text)

struct EventMask {
	int        flag;
	const char *name;

};

int freadsome(void *dest, size_t remain, FILE *file)
{
	char *offset = (char*)dest;
	while (remain) {
		int n = fread(offset, 1, remain, file);
		if (n==0) {
			return -1;
		}

		remain -= n;
		offset += n;
	}
	return 0;
}

int main(int argc, char *argv[])
{
	const char *target;
	if (argc == 1) {
		target = ".";
	} else {
		target = argv[1];
	}

	EventMask event_masks[] = {
           {IN_ACCESS        , "IN_ACCESS"}        ,  
           {IN_ATTRIB        , "IN_ATTRIB"}        ,  
           {IN_CLOSE_WRITE   , "IN_CLOSE_WRITE"}   ,  
           {IN_CLOSE_NOWRITE , "IN_CLOSE_NOWRITE"} ,  
           {IN_CREATE        , "IN_CREATE"}        ,  
           {IN_DELETE        , "IN_DELETE"}        ,  
           {IN_DELETE_SELF   , "IN_DELETE_SELF"}   ,  
           {IN_MODIFY        , "IN_MODIFY"}        ,  
           {IN_MOVE_SELF     , "IN_MOVE_SELF"}     ,  
           {IN_MOVED_FROM    , "IN_MOVED_FROM"}    ,  
           {IN_MOVED_TO      , "IN_MOVED_TO"}      ,  
           {IN_OPEN          , "IN_OPEN"}          ,  

           {IN_DONT_FOLLOW   , "IN_DONT_FOLLOW"}   ,  
           {IN_EXCL_UNLINK   , "IN_EXCL_UNLINK"}   ,  
           {IN_MASK_ADD      , "IN_MASK_ADD"}      ,  
           {IN_ONESHOT       , "IN_ONESHOT"}       ,  
           {IN_ONLYDIR       , "IN_ONLYDIR"}       ,  

           {IN_IGNORED       , "IN_IGNORED"}       ,  
           {IN_ISDIR         , "IN_ISDIR"}         ,  
           {IN_Q_OVERFLOW    , "IN_Q_OVERFLOW"}    ,  
           {IN_UNMOUNT       , "IN_UNMOUNT"}       ,  
	};

	int monitor = inotify_init();
	if ( -1 == monitor ) {
		ERROR("monitor");
	}

	int watcher = inotify_add_watch(monitor, target, IN_ALL_EVENTS);
	if ( -1 == watcher  ) {
		ERROR("inotify_add_watch");
	}

	FILE *monitor_file = fdopen(monitor, "r");
	char last_name[1024];
	char name[1024];

	/* event:inotify_event -> name:char[event.len] */
	while (true) {
		inotify_event event;
		if ( -1 == freadsome(&event, sizeof(event), monitor_file) ) {
			ERROR("freadsome");
		}
		if (event.len) {
			freadsome(name, event.len, monitor_file);
		} else {
			sprintf(name, "FD: %d\n", event.wd);
		}

		if (strcmp(name, last_name) != 0) {
			puts(name);
			strcpy(last_name, name);
		}

		/* 显示event的mask的含义 */
		for (int i=0; i<sizeof(event_masks)/sizeof(EventMask); ++i) {
			if (event.mask & event_masks[i].flag) {
				printf("\t%s\n", event_masks[i].name);
			}
		}
	}
	return 0;
}
运行结果: vim 中 对监测目录中的 inotify.cc 进行保存
4913
	IN_CREATE
	IN_OPEN
	IN_ATTRIB
	IN_CLOSE_WRITE
	IN_DELETE
inotify.cc
	IN_MOVED_FROM
inotify.cc~
	IN_MOVED_TO
inotify.cc
	IN_CREATE
	IN_OPEN
	IN_MODIFY
	IN_CLOSE_WRITE
	IN_ATTRIB
inotify.cc~
	IN_DELETE
可以看到,vim保存文件的流程为
创建一个4913文件用于检测vim对目录的控制权限
把inotify.cc更名为inotify.cc~
新建inotify.cc,并写入buffer
删除inotify.cc~

5. 相关链接

man inotify

-----------------------------------------------------------------------------------------------------------------------------------------------------------

使用inotify监控linux文件系统变化

2010-04-07

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://tianyapiaozi.blogbus.com/logs/61783047.html

inotify是linux系统2.6.13以后内核支持的一种特性,功能是监视文件系统的变化,在监听到文件系统变化后会向相应的应用程序发送事件。典型应用场景比如文件管理器,理想情况下应该在用户使用其他手段修改了文件夹的内容后马上反映出最新的内容,而不应该在用户手动刷新后才显示最新内容。如果没有类似inotify的机制,一般会采用轮询的方式实现这种功能,不能在第一时间反映文件系统变化而且浪费CPU时间。

inotify支持的文件系统事件有:

  • IN_ACCESS,文件被访问
  • IN_ATTRIB,文件属性被修改
  • IN_CLOSE_WRITE,可写文件被关闭
  • IN_CLOSE_NOWRITE,不可写文件被关闭
  • IN_CREATE,文件/文件夹被创建
  • IN_DELETE,文件/文件夹被删除
  • IN_DELETE_SELF,被监控的对象本身被删除
  • IN_MODIFY,文件被修改
  • IN_MOVE_SELF,被监控的对象本身被移动
  • IN_MOVED_FROM,文件被移出被监控目录
  • IN_MOVED_TO,文件被移入被监控目录
  • IN_OPEN,文件被打开

inotify涉及的主要API有:

1 int inotify_init( void);
2 int inotify_add_watch( int fd , const char * pathname , uint32_t mask);
3 int inotify_rm_watch( int fd , uint32_t wd);
  • inotify_init() 在内核中创建 inotify 子系统的一个实例,并返回一个文件描述符。还有一个类似的函数inotify_init1,能够提供一些额外的控制。
  • inotify_add_watch() 用于向与inotify实例相关联的监视列表添加新监视器或修改已有的监视器。每个监视器必须提供一个路径名和相关事件的列表。如果 inotify_add_watch() 成功,该调用会为已注册的监视器返回一个惟一的标识符;否则,返回 -1。
  • inotify_rm_watch() 则删除一个监视器。

为了确定哪些文件系统事件发生,需向inotify_init返回的文件描述符进行read系统调用。成功的read调用会返回一个或多个事件,事件的结构如下:

1 struct inotify_event {
2     int      wd;       /* Watch descriptor */
3     uint32_t mask;     /* Mask of events */
4     uint32_t cookie;   /* Unique cookie associating related events (for rename */
5     uint32_t len;      /* Size of name field */
6     char     name [];   /* Optional null-terminated name */
7 };

其中,wd用来标识与该事件相关联的监视器。mask标识哪些事件发生。name域只有当事件是与监控目录中的文件相关时才会使用,它包含相对于监视目录的文件名字。len保存着name域的字节数。因此,每个inotify_event结构体的大小为sizeof(inotify_event)+len。

通过 read 调用可以一次获得多个事件,只要提供的 buf 足够大。
                size_t len = read (fd, buf, BUF_LEN);
buf 是一个 inotify_event 结构的数组指针,BUF_LEN 指定要读取的总长度,buf 大小至少要不小于 BUF_LEN,该调用返回的事件数取决于 BUF_LEN 以及事件中文件名的长度。len 为实际读去的字节数,即获得的事件的总长度。
可以在函数 inotify_init() 返回的文件描述符 fd 上使用 select() 或poll(), 也可以在 fd 上使用 ioctl 命令 FIONREAD 来得到当前队列的长度。close(fd)将删除所有添加到 fd 中的 watch 并做必要的清理。


下面是一个简单的实例:

01 #include <stdio.h>
02 #include <unistd.h>
03 #include <sys/inotify.h>
04 #define MAX_BUF_SIZE 1024
05
06 int main (){
07     int fd , wd;
08     int len , index;
09     char buffer [ 1024 ];
10     struct inotify_event * event;
11     char * path ="/tmp";
12
13     fd = inotify_init();
14     if( fd < 0 ){
15         printf( "Failed to initialize inotify. \n ");
16         return 1;
17     }
18     wd = inotify_add_watch( fd , path , IN_CLOSE_WRITE | IN_CREATE);
19     if( wd < 0 ){
20         printf( "Can't add watch for %s" , path);
21         return 1;
22     }
23     while( len = read( fd , buffer , MAX_BUF_SIZE )){
24         index = 0;
25         while( index < len ){
26             event = ( struct inotify_event *)( buffer + index);
27             if( event -> wd != wd)
28                 continue;
29             if( event -> mask & IN_CLOSE_WRITE)
30                 printf( "file %s is closed for write. \n " , event -> name);
31             index += sizeof( struct inotify_event) + event -> len;
32         }
33     }
34     return 0;
35 }

另外,可以通过安装inotify-tools,来获得可用于shell脚本的inotify命令行工具。


 

 类似资料:

相关文章

相关问答