Linux内核Inotify机制学习笔记

1、Inotify简介:java

Inotify是一种文件变化通知机制,Linux内核从2.6.13开始引入。它是一个内核用于通知用户空间程序文件系统变化的机制。开源社区提出用户态须要内核提供一些机制,以便用户态可以及时地得知内核
或底层硬件设备发生了什么,从而可以更好地管理设备,给用户提供更好的服务,如 hotplug、udev 和 inotify 就是这种需求催生的。
Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统可以利用它对设备进行有效的管理,
udev 动态地维护 /dev 下的设备文件,
inotify 是一种文件系统的变化通知机制,如文件增长、删除等事件能够马上让用户态得知。node

Inotify 是为替代 dnotify 而设计的,它克服了 dnotify 的缺陷,提供了更好用的,简洁而强大的文件变化通知机制:linux

(1) Inotify 不须要对被监视的目标打开文件描述符,并且若是被监视目标在可移动介质上,那么在 umount 该介质上的文件系统后,被监视目标对应的 watch 将被自动删除,而且会产生一个 umount 事件。
(2) Inotify 既能够监视文件,也能够监视目录。
(3) Inotify 使用系统调用而非 SIGIO 来通知文件系统事件。
(4) Inotify 使用文件描述符做为接口,于是可使用一般的文件 I/O 操做select 和 poll 来监视文件系统的变化。android

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)cookie

 

2、使用方法函数

经过inotify_init()建立一个文件描述符,而后使用inotify_add_watch()附加一个或多个监视器(一个监视器是一个路径和一组事件),接着使用 read()方法从描述符获取事件信息,read()函数在事件发生以前是被阻塞的。
也能够在函数inotify_init()返回的文件描述符fd 上使用 select() 或poll(), 也能够在fd上使用ioctl命令FIONREAD来获得当前队列的长度。close(fd)将删除全部添加到fd中的watch并作必要的清理。测试

系统调用接口:this

第一步 建立 inotify 实例spa

int fd = inotify_init ();

第二步 添加一个 watch.net

int wd = inotify_add_watch (fd, path, mask);

每个inotify 实例对应一个独立的排序的队列。文件系统的变化事件使用Watch对象来描述,每个Watch是一个二元组(目标,事件掩码),目标能够是文件或目录,事件掩码表示应用但愿关注的inotify 事件,每个位对应一个 inotify 事件。Watch经过文件或目录的路径名来添加。目录Watch将返回在该目录下的全部文件上面发生的事件。
参数:
fd 是 inotify_init() 返回的文件描述符,path是被监视的目标的路径名(即文件名或目录名),mask是事件掩码, 在头文件 linux/inotify.h 中定义了每一位表明的事件。可使用一样的方式来修改事件掩码,即改变但愿被通知的inotify 事件。

第三步 使用read系统调用读取处理事件

 

1.删除一个watch

int ret = inotify_rm_watch (fd, wd);

fd是inotify_init()返回的文件描述符句柄,wd是 inotify_add_watch()返回的watch描述符句柄。Ret是函数的返回值。文件事件用一个 inotify_event结构表示,它经过读取inotify_init()返回的文件描述符句柄来得到。

struct inotify_event { __s32 wd; /* 被监视目标的 watch 描述符 */ __u32 mask; /* 事件掩码 */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* name字符串的长度 */
    char            name[0];        /* 被监视目标的路径名 */ };

 

3、使用demo

#include <stdio.h> #include <sys/inotify.h> #include <unistd.h> #include <errno.h> #include <string.h>
 
/* * 指定一个目录,当目录中建立或者删除文件时,把相应的信息打印出来 * Usage : inotify <dir> */
int main(int argc, char *argv[]) { int fd; int result; char event_buf[512]; int event_pos = 0; struct inotify_event *event; struct inotify_event *pos_event; if(2 != argc) { printf("Usage : %s <dir>\n", argv[0]); return -1; } /* 1.初始化一个inotify的实例,得到一个该实例的文件描述符 */ fd = inotify_init(); if (fd == -1) { printf("inotify init error: %s", strerror(errno)); return -1; } /* 2.添加一个用于监视的目录: 监视该目录中文件的添加和移除修改 */ result = inotify_add_watch(fd, argv[1], IN_DELETE | IN_CREATE | IN_MODIFY); if(-1 == result) { printf("inotify_add_watch error:%s\n", strerror(errno)); return -1; } /* 不停的监视当前目录中是否有添加或者删除文件 */
    while(1) { /* 读取inotify的监视事件的信息 */ memset(event_buf, 0, sizeof(event_buf)); pos_event = (struct inotify_event *)event_buf; /* 阻塞读取 */ result = read(fd, event_buf, sizeof(event_buf)); if (result < (int)sizeof(struct inotify_event)) { printf("could not read event: %s\n",strerror(errno)); return -1; } /* 将得到的inotify信息打印出来 */
        while (result >= (int)sizeof(struct inotify_event)) { event = pos_event; if (event->len) { if (event->mask & IN_CREATE) { printf("create : file is %s\n", event->name); } else if (event->mask & IN_DELETE) { printf("delete : file is %s\n", event->name); } else if (event->mask & IN_MODIFY) { printf("modify : file is %s\n", event->name); } } /* 更新位置信息,以便得到下一个 inotify_event 对象的首地址 */ pos_event++; result -= (int)sizeof(struct inotify_event); } } /* 关闭这个文件描述符 */ close(fd); return 0; }

测试:

# ./inotify_test /mytest/ & # touch a.txt create : file is a.txt # cp /etc/profile ./ create : file is profile modify : file is profile # t# echo hello > a.txt modify : file is a.txt modify : file is a.txt # # echo good >> a.txt modify : file is a.txt # 

 

4、内核实现

1.在内核中,每个inotify实例对应一个 inotify_device 结构,inotify_device在用户态调用inotify_init()时建立,当关闭 inotify_init()返回的文件描述符时将被释放。
2.inotify_watch结构在用户态调用inotify_add_watch()时建立,在用户态调用inotify_rm_watch()或close(fd)时被释放。不管是目录仍是文件,在内核中都对应一个inode结构,inotify 系统在inode结构中增长了两个字段:

#ifdef CONFIG_INOTIFY struct list_head    inotify_watches; /* watches on this inode */
    struct semaphore    inotify_sem;    /* protects the watches list */
#endif

inotify_watches 指向被监视目标上的watch列表,每当用户调用inotify_add_watch()时,内核就为添加的watch建立一个inotify_watch结构,并把它插入到被监视目标对应的inode的inotify_watches列表。inotify_sem用于同步对inotify_watches列表的访问。

3.当文件系统发生事件时,相应的文件系统将显示调用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_inode_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上。

 

五.Android中对inotify的使用

Android的文件观察Observer机制就是在Linux文件系统的Inotify机制上实现的

FileObserver.java
    |
android_util_FileObserver.cpp
    |
fs/notify/inotify

 

 

 

 

参考:

https://blog.csdn.net/yangwen123/article/details/13996361

相关文章
相关标签/搜索