• inotify递归监控 和 inotify的限制与警告


    1 inotify基本流程

    1.1. inotify基本使用方式

    Inotify 的基本使用方式是通过 inotify_init 初始化句柄得到描述符fd,通过inotify_add_watch向句柄注册监控路径和监控事件类型。再监听fd,事件发生时,fd会变得可读,通过read从fd中读取以 struct inotify_event 类型的数组返回的数据,即可知道发生对应事件的目录的观察描述符,事件对应的文件或文件夹名称以及具体的事件。

    1.2. man inotify提供的示例

    inotify返回事件的结构体,其中wd即为观察描述符,在inotify_add_watch调用时返回,需在调用层对路径和wd做关联,才能在事件发生时得到绝对路径,因为name只有文件或文件夹名。mask则返回了具体的事件

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

    man page中示例的核心部分如下,示例使用poll,对命令行参数输入的多个路径通过inotify进行监听

     static void
    	handle_events(int fd, int *wd, int argc, char* argv[])
    	{
    	   char buf[4096]
    		   __attribute__ ((aligned(__alignof__(struct inotify_event))));
    	   const struct inotify_event *event;
    	   
    	    for (;;) {
    		   len = read(fd, buf, sizeof buf);
    for (ptr = buf; ptr < buf + len;
    				   ptr += sizeof(struct inotify_event) + event->len) {
    
    			event = (const struct inotify_event *) ptr;
    
    			/* Print event type */
    			if (event->mask & IN_OPEN)
    			   printf("IN_OPEN: ");
    			if (event->mask & IN_CLOSE_NOWRITE)
    			   printf("IN_CLOSE_NOWRITE: ");
    			if (event->mask & IN_CLOSE_WRITE)
    			   printf("IN_CLOSE_WRITE: ");
    
    			/* Print the name of the watched directory 打印wd对应的根目录*/
    			for (i = 1; i < argc; ++i) {
    			   if (wd[i] == event->wd) {
    				   printf("%s/", argv[i]);
    				   break;
    			   }
    			}
    
    			/* Print the name of the file */
    			if (event->len)
    			   printf("%s", event->name);
    
    			/* Print type of filesystem object */
    			if (event->mask & IN_ISDIR)
    			   printf(" [directory]\n");
    			else
    			   printf(" [file]\n");
    			}
    		}
    	}
    	
     int
    	main(int argc, char* argv[])
    	{
            int fd, i, poll_num;
            int *wd;
            nfds_t nfds;
            struct pollfd fds[2];
    		...
    		fd = inotify_init1(IN_NONBLOCK);
    		...
    		for (i = 1; i < argc; i++) {
    			wd[i] = inotify_add_watch(fd, argv[i], IN_OPEN | IN_CLOSE);
    			...
    		}
    		...
    		/* Inotify input */
            fds[1].fd = fd;
            fds[1].events = POLLIN;
    		
    		...
    		while (1) {
    		...
    		poll_num = poll(fds, nfds, -1);
    		if (poll_num > 0) {
    			...
    			if (fds[1].revents & POLLIN) {
    			   /* Inotify events are available */
    			   handle_events(fd, wd, argc, argv);
    			}
    		}		
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    2 inotify监控递归监控原理

    如上所述这里的事件只针对注册了的监控路径,而无法覆盖到子路径,故而如果需要递归监控,就需要把所有的子路径都向对应的fd进行注册以实现监控,在通过inotify_add_watch进行注册时,会返回一个观察描述符,用于与对应的路径相对应,在fd可读返回响应事件时,返回的数组数据会包含这个观察描述符,预先存储了子路径和观察描述符的字典,就可以获取到子路径,和上述通过wd获取监听路径的原理一致。

    同时有多个路径不同的根路径需要监控,那么合理的抽象是形成 根路径管理对象——根路径对象(包含子路径)这两级结构,每个根路径使用独立的inotify_init初始化得到的fd,以对应fd的字典的形式存储监控的根路径。可以构建如下的数据结构。
    在这里插入图片描述

    3 inotify递归监控基本实现

    针对递归监控的流程的代码的主要流程实现如下,如下覆盖为递归对特定目录加入到inotify_add_watch的流程。

    int inotify_monitor_dir::ino_monitor_dir_recurse(const char * szPath){
    	struct dirent *pdirent;
    	char szSubPath[1024] = {0};
    	int iwd = 0;
    
    	iwd  = inotify_add_watch (inotifyfd, szPath,
        	IN_MODIFY | IN_CREATE | IN_DELETE);
    	
    	subdir_map[iwd] = std::string(szPath);
    	
    	DIR *d_info =  opendir(szPath);
    	if (d_info)	{
    		while ((pdirent = readdir(d_info)) != NULL)	{					
    			if(!strcmp(pdirent->d_name,"..") || !strcmp(pdirent->d_name,".")  ){
    				continue;
    			}
    			
    			if (pdirent->d_type & DT_DIR)
    			{
    				snprintf(szSubPath,sizeof(szSubPath),"%s/%s",szPath,pdirent->d_name);
    				ino_monitor_dir_recurse(szSubPath);
    			}
    		}
    		closedir(d_info);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4 inotify 知识补充

    4.1 修改inotify监控事件

    根据inotify_add_watch的定义,以不同的mask来调用inotify_add_watch,就可以修改在对应路径上的监听事件,同时该修改不会引起磁盘被读写。

    4.2 inotify的限制和警告(Limitations and caveats)

    在man inotify中对inotify的限制和警告做了描述,如下为简单翻译。个人认为对任何技术的了解,了解其局限性都是非常重要的

    The inotify API provides no information about the user or process that triggered the inotify event. 
     In particular, there is no easy way for a process that is monitoring events via  inotify to distinguish events
      that it triggers itself from those that are triggered by other processes.
    
    inotify相关的API未提供任何与触发了这个inotify事件相关的用户或者进程的信息。
    特意需要指出的是,并没有一种简单的方法可以让一个在监控事件的进程,通过inotify来区分是这个进程自己还是其他进程触发了事件。
    
    Inotify reports only events that a user-space program triggers through the filesystem API.  
    As a result, it does not catch remote events that occur on network filesystems.  
    (Applicationsmust fall back to polling the filesystem to catch such events.)  
    Furthermore, various pseudo-filesystems such as /proc, /sys, and /dev/pts are not monitorable with inotify.
    
    inotify只会报告用户空间程序通过文件系统的api触发的事件。
    所以他无法抓取到任何网络文件系统上发生的事件
    (应用程序只有通过论询文件系统来获取相关事件)
    同时伪文件系统比如/proc,/sys ,/dev/pts都无法被inotify监控。
    
    The inotify API does not report file accesses and modifications that may occur because of mmap(2), msync(2), and munmap(2).
    
    mmap(2), msync(2), and munmap(2).系统调用导致的文件修改和访问是无法被监控到的。
    
    The inotify API identifies affected files by filename.  
    However, by the time an application processes an inotify event, the filename may already have been deleted or renamed.
    
    inotify的API根据文件名来识别受影响的文件,
    但实际上在应用程序执行event事件时,对应的文件可能已经被删除或者重命名了。
    
    The inotify API identifies events via watch descriptors.  
    It is the application's responsibility to cache a mapping (if one is needed) between watch descriptors and pathnames.
    Be  aware  that directory renamings may affect multiple cached pathnames.
    
    inotify API只通过观察描述符来区分事件,
    应用程序需要自己去维护一个观察描述符和路径之间的映射,
    需要注意文件名的变更,会影响多个缓存的路径名。
    
    Inotify  monitoring  of  directories is not recursive: 
    to monitor subdirectories under a directory, additional watches must be created. 
     This can take a significant amount time for large directory trees.
    
    inotify的监控不是递归的(所以需要设计上面的流程)。
    要监控子文件夹,需要创建大量的观察描述符
    如果是一个庞大的目录树,这会耗费大量的时间。
    
    If monitoring an entire directory subtree, 
    and a new subdirectory is created in that tree or an existing directory is renamed into that tree,
     be aware that by the time you create a watch
    for  the  new  subdirectory, new files (and subdirectories) may already exist inside the subdirectory.
    Therefore, you may want to scan the contents of the subdirectory immediately after
    adding the watch (and, if desired, recursively add watches for any subdirectories that it contains).
    
    如果要监控一个目录的完整的目录树,
    然后一个新的子文件夹被创建,或者一个现有的文件夹被重命名,
    需要注意当你为新的子文件夹创建一个新的观察时,
    子文件和子文件夹可能已经在子目录下存在了。
    因此,你还需要在添加观察之后
    ,立即遍历子文件夹的内容。(如果需要,还需要递归观察子文件夹的所有内容)
    
    Note that the event queue can overflow.  
    In this case, events are lost.  
    Robust applications should handle the possibility of lost events gracefully.  
    For example, it may be necessary to
    rebuild  part or all of the application cache. 
     (One simple, but possibly expensive, approach is to close the inotify file descriptor, empty the cache, 
     create a new inotify file descriptor, 
     and then re-create watches and cache entries for the objects to be monitored.)
    
    注意事件队列是可能溢出的,
    在这种情况下,
    事件就丢失了。
    健壮的程序需要能够优雅地处理可能发生的溢出。
    比如说,可能会需要部分或全部地重建应用层的缓存,
    (一个简单,但是可能昂贵的方法,是关闭inotify的描述符,清空缓存,
    然后重建一个新的描述符,
    然后重新为所有的要观察的对象创建的观察点和缓存条目。)
    
    If a filesystem is mounted on top of a monitored directory, 
    no event is generated, and no events are generated for objects immediately under the new mount point.
      If  the  filesystem  issubsequently unmounted,
       events will subsequently be generated for the directory and the objects it contains.
    
    如果文件系统被挂载到一个被监控的根目录下,
    没有事件会发生,同时在新的挂载点下发生的事件也不会被捕捉到(这段我个人不是完全理解)。
    如果随后文件系统被卸载了,
    则与文件系统相关的目录和事件也会在随后被捕捉(同上,并不是完全理解。)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
  • 相关阅读:
    elementUI 特定分辨率(如1920*1080)下el-row未超出一行却换行
    [OtterCTF 2018] 电子取证
    功率放大器主要性能指标是什么(功率放大器工作状态的分类)
    数仓建设(三)
    springcloud eureka DS Replicas
    新建一个Python Jupyter Notebook的运行环境
    版权和商标的那些事
    简单的链接中心软件yal
    Electron常见问题 63 - sentry上传自定义pdb符号表
    小项目中怎么防止Vue的闪现画面效果
  • 原文地址:https://blog.csdn.net/u010050543/article/details/126560859