1.inotify的主要功能是内核用来通知用户空间程序文件系统变化的一种机制。众所周知,Linux桌面系统与MAC或者Windows相比,有很多不尽如人意的地方。为了改善这种情况,开源社区提出,用户态需要内核提供一些机制,使得用户态能够及时获知内核或底层硬件设备的信息。什么,以便更好地管理设备,为用户提供更好的服务,hotplug、udev、inotify等就是在这种需求下诞生的。热插拔是内核将一些关于热插拔设备的事件通知用户态应用程序的机制。桌面系统可以使用它来有效地管理设备。udev在/dev下动态维护设备文件,inotify是一个文件系统。变更通知机制,如文件的增加、删除等事件,可以让用户第一时间知道。该机制由著名的桌面搜索引擎项目beagle引入,并在Gamin等项目中得到应用。2.用户界面在用户模式下,通过三个系统调用和文件I/操作返回的文件描述符来使用inotify。使用inotify的第一步是创建一个inotify实例:intfd=inotify_init();每个inotify实例对应于一个单独排序的队列。文件系统的更改事件由一个名为watches的对象管理。每个手表都是一个二元组(目标、事件掩码)。目标可以是文件或目录。事件掩码表示应用程序想要关注的inotify事件。每个Bit对应一个inotify事件。监视对象由监视描述符引用,监视由文件或目录路径名添加。目录监视将返回发生在该目录中所有文件上的事件。以下函数用于添加手表:intwd=inotify_add_watch(fd,path,mask);fd是inotify_init()返回的文件描述符,path是被监控目标的路径名(即文件名或目录名),mask是事件掩码,每一位代表的事件定义在头文件linux/inotify.h中。H。您可以使用相同的方式修改事件掩码,即更改您希望收到通知的inotify事件。Wd是监视描述符。以下函数用于删除手表:intret=inotify_rm_watch(fd,wd);fd是inotify_init()返回的文件描述符,wd是inotify_add_watch()返回的watch描述符。Ret是函数的返回值。文件事件由inotify_event结构表示,它是通过使用inotify_init()返回的文件描述符获得的,使用通常的文件读取函数读取:structinotify_event{__s32wd;/*watchdescriptor*/__u32mask;/*watchmask*/__u32cookie;/*cookietosynchronizetwoevents*/__u32len;/*length(includingnulls)ofname*/charname[0];/*stubforpossiblename*/};结构体中,wd是被监控目标的watch描述符,mask是事件掩码,len是name字符串长度,name是被监控目标的路径名,这个结构体的name域是一个stub,它是仅供用户参考文件名,文件名是可变长度的,它实际上遵循结构,文件名将被0填充以使下一个事件结构4字节对齐。注意len也计算填充字节。只要提供的buf足够大,就可以通过read调用一次获取多个事件。size_tlen=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的手表并进行必要的清理。intinotify_init(无效);intinotify_add_watch(intfd,constchar*path,__u32mask);intinotify_rm_watch(intfd,__u32mask);3.内核实现原理在内核中,每个inotify实例对应一个inotify_device结构:structinotify_device{wait_queue_head_twq;/*waitqueuefori/o*/structidridr;/*idrmappingwd->watch*/structsemaphoresem;/*protectsthisbadboy*/structlist_headevents;/*listofqueuedevents*/structlist_headwatches;/*listofwatches*/atomic_tcount;/*referencecount*/structuser_struct*user;/*userwhoopenedthisdevint*/unsized;/*sizeofthequeue(bytes)*/unsignedintevent_count;/*numberofpendingevents*/unsignedintmax_events;/*maximumnumberofevents*/u32last_wd;/*thelastwdallocated*/};d_list指向所有inotify_device组成的list,i_list指向所有监听的inode组成的list,count为引用计数,dev指向watch所在inotify实例对应的inotify_device结构体,inode指向要监听的inode被watch监听,wd是分配给watch的描述符,mask是watch的事件掩码,表示它对哪些文件系统事件感兴趣。inotify_device结构体在用户态调用inotify_init()时创建,并在inotify_init()返回的文件描述符关闭时释放。inotify_watch结构体在用户态调用inotify_add_watch()时创建,在用户态调用inotify_rm_watch()或close(fd)时释放。无论是目录还是文件,在内核中都对应着一个inode结构。inotify系统在inode结构中增加了两个字段:structinotify_watch{structlist_headd_list;/*entryininotify_device'slist*/structlist_headi_list;/*entryininode'slist*/atomic_tcount;/*referencecount*/structinotify_device*dev;/*associateddevice*/structinode*inode;/*associateddinode*/s32wd;/*watchdescriptor*/u32mask;/*eventmaskforthiswatch*/};d_list指向所有inotify_device组成的列表,i_list指向所有被监听的inode列表,count为引用计数,dev指向watch所在inotify实例对应的inotify_device结构体,inode指向待监听的inodewatch监听,wd是分配给watch的描述符,mask是watch的事件掩码,表示它对哪些文件系统事件感兴趣。结构体inotify_device在用户态调用inotify_init()时创建,并将当inotify_init()返回的文件描述符关闭时被释放。inotify_watch结构体在用户态调用inotify_add_watch()时创建,在用户态调用inotify_rm_watch()或close(fd)时释放。无论是目录还是文件,在内核中都对应着一个inode结构。inotify系统在inode结构中增加了两个字段:#ifdefCONFIG_INOTIFYstructlist_headinotify_watches;/*watchesonthisinode*/structsemaphoreinotify_sem;/*protectsthewatcheslist*/#endifinotify_watches是被监控的目标上面的watchlist,每当用户调用inotify_add_watch()时,内核都会创建一个inotify_watch结构体,将其插入到被监控目标对应的inode的inotify_watches列表中。inotify_sem用于同步对inotify_watches列表的访问。当文件系统中发生***部分提到的事件之一时,相应的文件系统代码将显示并调用fsnotify_*将相应的事件报告给inotify系统,其中*号为相应的事件名称。当前实现包括:fsnotify_move,文件从一个目录移动到另一个目录fsnotify_nameremove,文件从目录中删除fsnotify_inoderemove,自删除fsnotify_create,创建新文件fsnotify_mkdir,创建新目录fsnotify_access,文件读取fsnotify_modify,文件写入fsnotify_open,文件打开fsnotify_close,文件关闭fsnotify_xattr,文件的扩展属性被修改fsnotify_change,文件被修改或原始数据被修改。有一个例外,就是inotify_unmount_inodes,当文件系统被umount时会调用它,将umount事件通知给inotify系统。上面提到的通知函数都调用了inotify_inode_queue_event(inotify_unmount_inodes直接调用了inotify_dev_queue_event),这个函数首先判断对应的inode是否被监听,通过检查inotify_watches列表是否为空来实现,如果发现inode没有被监听,则无如果没有,立即返回,否则,遍历inotify_watches列表,查看当前文件操作事件是否被某个watch监听,如果是,则调用inotify_dev_queue_event,否则,返回。inotify_dev_queue_event函数首先判断该事件是否是前一个事件的重复,如果是则丢弃该事件并返回,否则判断inotify实例即inotify_device的事件队列是否溢出。如果溢出,则产生溢出事件,否则产生当前事件。这些事件是由kernel_event构造的,kernel_event会创建一个inotify_kernel_event结构体,然后将该结构体插入到对应inotify_device的events事件列表中,然后唤醒inotify_device结构体中wq指向的等待队列。一个想要监听文件系统事件的用户模式进程在inotify实例(即inotify_init()返回的文件描述符)上调用read时挂在等待队列wq上但是没有事件。4.使用示例下面是使用inotify监控文件系统事件的示例:#include#include#include_syscall0(int,inotify_init)_syscall3(int,inotify_add_watch,int,fd,constchar*,path,__u32,mask)_syscall2(int,inotify_rm_watch,int,fd,__u32,mask)char*monitored_files[]={"./tmp_file","./tmp_dir","/mnt/sda3/windows_file"};structwd_name{intwd;char*name;};#defineWD_NUM3structwd_namewd_array[WD_NUM];char*e??vent_array[]={“文件被访问”,“文件被修改”,“文件属性被更改”,“可写文件关闭”,“不可写文件关闭”,“文件被打开”,“文件从X移动”,“文件被移动”,“文件子文件被删除”"Selfwasdeleted","Selfwasmoved","","Backingfswasunmounted","Eventqueuedoverflowed","Filewasignored"};#defineEVENT_NUM16#defineMAX_BUF_SIZE1024intmain(void){intfd;intwd;charbuffer[1024];char*offset=NULL;structinotify_event*event;intlen,tmp_len;charstrbuf[16];inti=0;fd=inotify_init();if(fd<0){printf("Failtoinitializeinotify.\n");exit(-1);}for(i=0;i
