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命令行工具。