最近在写一个云备份的项目,其中有一个模块是要监控计算机本地的文件,于是我翻了翻linux/unix系统编程手册发现了inotify这个用于文件监控的框架

1.概述

1)inotify机制可用于监控文件或目录。当监控目录时,与该目录自身以及该目录下面的文件都会被监控,其上有事件发生时都会通知给应用程序

2)inotify监控机制为非递归,若应用程序有意监控整个目录子树内的事件,则需对该树中的每个目录发起inotify_add_watch()调用

3)可使用select(),poll(),epoll()以及由信号驱动的I/O来监控inotify文件描述符

2.inotify API

1)inotify_init()系统调用可创建一新的inotify实例

  1. #include<sys/inotify.h>
  2. int inotify_init(void);

该函数的返回值为一个文件描述符,我们可以简单的理解为该文件描述符所指代的文件中将会保存类似所监控的目录所发生的事件集

2)针对fd所指的inotify实例的监控列表,系统调用inotify_add_watch()可以追加新的监控项

  1. #include<sys/inotify.h>
  2. int inotify_add_watch(int fd,const char *pathname,uint32_t mask);

参数pathname为想要创建的监控项所对应的文件,特别注意调用该接口必须要对该文件有读权限,该函数只对文件做一次检查,如果在监控时修改了所监控的文件读权限,则不会影响继续监控此文件

参数mask为一位掩码,针对pathname定义了想要监控的事件,此函数的返回值为一个用于唯一指代此监控项的描述符

3.inotify事件

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不为目录时会失败

对于上述一些事件的说明:

1)当文件的元数据(比如,权限,所有权,链接计数,扩展属性,用户ID,或组ID等)改变时,会发生IN_ATTRIB事件

2)删除受监控对象时会发生IN_DELETE_SELF

3)重命名对象时会发生IN_MORE_SELF事件

4)ONE_SHOT允许只监控pathname的一个事件,事件发生后,监控项会自动从监控列表消失

4.读取inotify事件

将监控项项在监控列表中登记后,应运程序可用read()从inotify的文件描述符中读取事件,以判定发生了那些事件。若读取之时还没有发生任何事件,则read()会阻塞,直至有事件产生,事件发生后,每次调用read()会返回一个缓存区,内含一个或多个如下类型的结构体

  1. struct inotify_event
  2. {
  3. <span style="white-space:pre">    </span>int wd;
  1. <span style="white-space:pre">    </span>uint32_t mask;
  1. <span style="white-space:pre">    </span>uint32_t cookie;
  1. <span style="white-space:pre">    </span>uint32_t len;
  1. <span style="white-space:pre">    </span>char name[];
  2. }

.字段wd指名发生事件的是哪个监控描述符,该字段值由之前对inotify_add_watch()的调用返回。因为用read()读到的是inotify文件中的所有事件,可是当inotify同时监控多个目录或文件时我们应该如何区分读到的内容呢,这是wd就派上了用场,我们可以用wd来做区分

.mask字段返回了描述该事件的位掩码

.cookie字段将相关事件联系到一起,目前只有对重命名时才会用到

.len字段表示实际分配给name字段的字节数

.name字段则是标示该文件

5.简单程序事例

    1. #include<stdio.h>
    2. #include<assert.h>
    3. #include<unistd.h>
    4. #include<stdlib.h>
    5. #include<errno.h>
    6. #include<string.h>
    7. #include<sys/types.h>
    8. #include<sys/inotify.h>
    9. #include<limits.h>
    10. #include<fcntl.h>
    11. #define BUF_LEN 1000
    12. void displayInotifyEvent(struct inotify_event *i)
    13. {
    14. printf("  wd = %2d; ",i->wd);
    15. if(i->cookie > 0)
    16. {
    17. printf("cokkie = %4d; ",i->cookie);
    18. }
    19. printf("mask = ");
    20. if(i->mask & IN_ACCESS)   printf("IN_ACCESS\n");
    21. if(i->mask & IN_DELETE_SELF)   printf("IN_DELETE_SELF\n");
    22. if(i->mask & IN_MODIFY)  printf("IN_MODIFY\n");
    23. if(i->mask & IN_OPEN)   printf("IN_OPEN\n");
    24. if(len > 0)
    25. {
    26. printf("name = %s\n",i->name);
    27. }
    28. }
    29. int main(int argc,char **argv)
    30. {
    31. int inotifyFd,wd,j;
    32. char buf[BUF_LEN];
    33. ssize_t numRead;
    34. char *p;
    35. struct inotify_event *event;
    36. int flags;
    37. if(argc < 2 )
    38. {
    39. printf("error\n");
    40. }
    41. inotifyFd = inotify_init();
    42. if(inotifyFd == -1)
    43. {
    44. printf("初始化失败");
    45. }
    46. wd = inotify_add_watch(inotifyFd,argv[1],IN_ALL_EVENTS);
    47. if(wd == -1)
    48. {
    49. printf("error\n");
    50. }
    51. printf("Watching %s using wd %d\n",argv[1],wd);
    52. while(1)
    53. {
    54. numRead = read(inotifyFd,buf,BUF_LEN);
    55. if(numRead == -1)
    56. {
    57. printf("read error\n");
    58. }
    59. printf("Read %ldbytes from inotify fd\n",(long)numRead);
    60. for(p=buf;p < buf+numRead;)
    61. {
    62. event = (struct inotify_event *)p;
    63. displayInotifyEvent(event);
    64. p+=sizeof(struct inotify_event) + event->len;
    65. }
    66. }
    67. return 0;
    68. }
05-11 19:33