zoukankan      html  css  js  c++  java
  • inotify

    一、简介

    Inotify 是一个 Linux 内核特性,它监控文件系统,并且及时向专门的应用程序发出相关的事件警告,比如删除、读、写和卸载操作等。您还可以跟踪活动的源头和目标等细节。在实际项目中,如果项目带有配置文件,那么怎么让配置文件的改变和项目程序同步而不需要重启程序呢?一个明显的应用是:在一个程序中,使用Inotify监视它的配置文件,如果该配置文件发生了更改(更新,修改)时,Inotify会产生修改的事件给程序,应用程序就可以实现重新加载配置文件,检测哪些参数发生了变化,并在应用程序内存的一些变量做相应的修改。当然另一种方法可以是通过cgi注册命令,并通过命令更新内存数据及更新配置文件

    Inotify 可以监视的文件系统事件包括:
    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,创建新文件
    IN_DELETE,文件被删除,如 rm
    IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
    IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
    IN_UNMOUNT,宿主文件系统被 umount
    IN_CLOSE,文件被关闭,等同于(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
    IN_MOVE,文件被移动,等同于(IN_MOVED_FROM | IN_MOVED_TO)
    注:上面所说的文件也包括目录。


    二、使用Inofity

    要使用 inotify,您必须具备一台带有 2.6.13 或更新内核的 Linux 机器(以前的 Linux 内核版本使用更低级的文件监控器dnotify)。如果您不知道内核的版本,请转到 shell,输入 uname -a


    您还可以检查机器的 /usr/include/sys/inotify.h 文件。如果它存在,表明您的内核支持 inotify。

    使用 inotify 很简单:创建一个文件描述符,附加一个或多个监视器(一个监视器 是一个路径和一组事件),然后使用 read()方法从描述符获取事件信息。read() 并不会用光整个周期,它在事件发生之前是被阻塞的。

    更好的是,因为 inotify 通过传统的文件描述符工作,您可以利用传统的 select() 系统调用来被动地监控监视器和许多其他输入源。两种方法 — 阻塞文件描述符和使用 select()— 都避免了繁忙轮询。

     

    Inotify 提供 3 个系统调用,它们可以构建各种各样的文件系统监控器:

    • int fd = inotify_init() 在内核中创建 inotify 子系统的一个实例,成功的话将返回一个文件描述符,失败则返回 -1。就像其他系统调用一样,如果 inotify_init() 失败,请检查 errno 以获得诊断信息。
    • 顾名思义, int wd = inotify_add_watch(fd,path,mask) 用于添加监视器。每个监视器必须提供一个路径名和相关事件的列表(每个事件由一个常量指定,比如 IN_MODIFY)。要监控多个事件,只需在事件之间使用逻辑操作符或 — C 语言中的管道线(|)操作符。如果 inotify_add_watch() 成功,该调用会为已注册的监视器返回一个惟一的标识符;否则,返回 -1。使用这个标识符更改或删除相关的监视器。
    • int ret = inotify_rm_watch(fd, wd) 删除一个监视器。

    此外,还需要 read() 和 close() 系统调用。如果描述符由 inotify_init() 生成,则调用 read() 等待警告。假设有一个典型的文件描述符,应用程序将阻塞对事件的接收,这些事件在流中表现为数据。文件描述符上的由 inotify_init() 生成的通用close() 删除所有活动监视器,并释放与 inotify 实例相关联的所有内存(这里也用到典型的引用计数警告。与实例相关联的所有文件描述符必须在监视器和 inotify 消耗的内存被释放之前关闭)。

    三、测试Inotify

    在文件 /usr/include/sys/inotify.h. 中,您可以找到事件结构的定义,它是一种 C 结构,如下所示:

     

    1. struct inotify_event   
    2. {  
    3.   int wd;       /* The watch descriptor */  
    4.   uint32_t mask;    /* Watch mask */  
    5.   uint32_t cookie;  /* A cookie to tie two events together */  
    6.   uint32_t len;     /* The length of the filename found in the name field */  
    7.   char name __flexarr;  /* The name of the file, padding to the end with NULs */      
    8. }  

    结构中的char name是不占空间的,相当于char name[0],所以sizeof(struct inotify_event)长度是16,实际上该结构数据的总长度应该是16+len,数据是紧跟在uinit32_t len后面的数据。

    测试代码如下:

     

    1. #include <stdio.h>//printf  
    2. #include <string.h> //strcmp  
    3. #include <sys/inotify.h>//inotify_init inotify_add_watch....  
    4. #include <sys/select.h>//select timeval  
    5. #include <unistd.h>//close  
    6.   
    7. #define EVENT_SIZE  ( sizeof (struct inotify_event) )  
    8. #define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )  
    9. #define ERR_EXIT(msg,flag)  {perror(msg);goto flag;}  
    10.   
    11. int main( int argc, char **argv )   
    12. {  
    13.     int length, i = 0;  
    14.     int fd;  
    15.     int wd;  
    16.     char buffer[BUF_LEN];  
    17.   
    18.     if((fd = inotify_init()) < 0)  
    19.         ERR_EXIT("inotify_init",inotify_init_err);  
    20.   
    21.     if( (wd = inotify_add_watch( fd, "/tmp",    IN_MODIFY | IN_CREATE | IN_DELETE ) ) < 0)  
    22.         ERR_EXIT("inofity_add_watch", inotify_add_watch_err);  
    23.       
    24.     fd_set rfd;  
    25.     struct timeval tv;  
    26.     tv.tv_sec = 0;  
    27.     tv.tv_usec = 10000;//10millsecond  
    28.     while(true)  
    29.     {  
    30.         int retval;  
    31.         FD_ZERO(&rfd);  
    32.         FD_SET(fd, &rfd);  
    33.         retval = select(fd + 1, &rfd, NULL, NULL, &tv);  
    34.         if(retval == 0) continue;  
    35.         else if(retval == -1)  
    36.             ERR_EXIT("select",select_err);  
    37.   
    38.         // retval > 0  
    39.         length = read( fd, buffer, BUF_LEN );    
    40.         if(length < 0)  
    41.             ERR_EXIT("read",read_err);  
    42.   
    43.         //length >= 0  
    44.         int i = 0;  
    45.         while ( i < length )   
    46.         {  
    47.             struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];  
    48.             if ( event->len )   
    49.             {  
    50.                 if ( event->mask & IN_CREATE )   
    51.                 {  
    52.                     if ( event->mask & IN_ISDIR )   
    53.                         printf( "The directory %s was created. ", event->name );         
    54.                     else  
    55.                         printf( "The file %s was created. ", event->name );  
    56.                     if(strcmp(event->name,"kill") == 0)  
    57.                         ERR_EXIT("success exit",success_exit);  
    58.   
    59.                 }  
    60.                 else if ( event->mask & IN_DELETE )   
    61.                 {  
    62.                     if ( event->mask & IN_ISDIR )   
    63.                         printf( "The directory %s was deleted. ", event->name );         
    64.                     else  
    65.                         printf( "The file %s was deleted. ", event->name );  
    66.                 }  
    67.                 else if ( event->mask & IN_MODIFY )   
    68.                 {  
    69.                     if ( event->mask & IN_ISDIR )  
    70.                         printf( "The directory %s was modified. ", event->name );  
    71.                     else  
    72.                         printf( "The file %s was modified. ", event->name );  
    73.                 }  
    74.             }else  
    75.             {  
    76.                 //TODO  
    77.                 //when only a file(not directory) is specified by add watch function, event->len's value may be zero, we can handle it here  
    78.             }  
    79.             i += EVENT_SIZE + event->len;  
    80.         }  
    81.     }  
    82. success_exit:  
    83.     ( void ) inotify_rm_watch( fd, wd );  
    84.     ( void ) close( fd );  
    85.     return 0;  
    86.   
    87. read_err:  
    88. select_err:  
    89. inotify_add_watch_err:  
    90.     ( void ) inotify_rm_watch( fd, wd );  
    91. inotify_init_err:  
    92.     ( void ) close( fd );  
    93.   
    94.     return -1;  
    95. }  

    以上代码需要注意的地方:

    
    1.如果在/tmp目录下touch kill文件,程序则会退出

    2.如果只有一个add watch 一个file,那么这个file的更改产生的event事件中event->len是为0,需要额外的处理,此代码省略了具体的处理过程,以注释代替

    3.如果监测的是文件或目录的更改,使用 echo "xxx" >> file,会产生一个event事件,而使用echo "xxx" > file 会产生两个event事件,查了相关的资料,可能是因为后者需要先清空file文件内容,造成第一次event事件,再将xxx写入file保存,造成了第二次的event事件。

  • 相关阅读:
    Centos 7.9 部署可道云
    shell简单检查URL
    TIME_WAIT和CLOSE_WAIT状态过多的分析与解决
    win10 关闭自动更新
    Python3 按backspace问题 ^H
    CentOS7设置笔记本合盖不休眠
    centos7 /boot/分区处理
    if __name__ == '__main__'
    在Linux中了解TCP包装器(/etc/hosts.allow&/etc/hosts.deny)
    华为路由器端口映射
  • 原文地址:https://www.cnblogs.com/flintlovesam/p/7743866.html
Copyright © 2011-2022 走看看