FileObserver交互图

3、既然iNotify这么牛x,它是怎么实现起来的?

  • inotify 实例对应一个 inotify_device 结构

struct inotify_device {
        wait_queue_head_t       wq;             /* wait queue for i/o */
        struct idr              idr;            /* idr mapping wd -> watch */
        struct semaphore        sem;            /* protects this bad boy */
        struct list_head        events;         /* list of queued events */
        struct list_head        watches;        /* list of watches */
        atomic_t                count;          /* reference count */
        struct user_struct      *user;          /* user who opened this dev */
        unsigned int            queue_size;     /* size of the queue (bytes) */
        unsigned int            event_count;    /* number of pending events */
        unsigned int            max_events;     /* maximum number of events */
        u32                     last_wd;        /* the last wd allocated */
};

  • wq 是等待队列,被 read 调用阻塞的进程将挂在该等待队列上
  • idr 用于把 watch 描述符映射到对应的 inotify_watch
  • sem 用于同步对该结构的访问
  • events 为该 inotify 实例上发生的事件的列表被,该 inotify 实例监视的所有事件在发生后都将插入到这个列表
  • watches 是给 inotify 实例监视的 watch 列表,notify_add_watch 将把新添加的 watch 插入到该列表
  • count 是引用计数
  • user 用于描述创建该 inotify 实例的用户
  • queue_size 表示该 inotify 实例的事件队列的字节数
  • event_count 是 events 列表的事件数
  • max_events 为最大允许的事件数
  • last_wd 是上次分配的 watch 描述符

watch 对应一个 inotify_watch 结构

struct inotify_watch {
        struct list_head        d_list; /* entry in inotify_device's list */
        struct list_head        i_list; /* entry in inode's list */
        atomic_t                count;  /* reference count */
        struct inotify_device   *dev;   /* associated device */
        struct inode            *inode; /* associated inode */
        s32                     wd;     /* watch descriptor */
        u32                     mask;   /* event mask for this watch */
};

  • d_list 指向所有 inotify_device 组成的列表的
  • i_list 指向所有被监视 inode 组成的列表
  • count 是引用计数
  • dev 指向该 watch 所在的 inotify 实例对应的 inotify_device 结构
  • inode 指向该 watch 要监视的 inode
  • wd 是分配给该 watch 的描述符
  • mask 是该 watch 的事件掩码
  • 结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。
  • 结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

再来看看inode 结构,系统在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 列表的访问
  • 当文件系统发生第一部分提到的事件之一时,相应的文件系统代码将显示调用fsnotify_* 来把相应的事件报告给 inotify 系统,其中*号就是相应的事件名,目前实现包括:

event

描述

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 上。

 

 

核心原理其实就是在inode上建立起一系列的监听机制。 

 

 

简单介绍FileObserver

FileObserver其实就是基于iNotify机制,在java层和c层实现了些巧妙的封装,开放给用户接口调用:

private native int init();
private native void observe(int fd);
private native int startWatching(int fd, String path, int mask);
private native void stopWatching(int fd, int wfd);

怎么把FileObserver用好

1、不能递归监视---不能监视目录的子目录的变动。

2、被监听目录/文件在startwatching方法调用时,必须是已经创建好。

3、接受到事件时,不要对event做任何处理,需要将event post出去使用,推荐使用HandlerThread。

4、被监听的目录/文件在监控期间删除,FileObserver不会接受到任何事件,需要重新部署监控。

5、FileObserver维护一个静态ObserverThread,应用增加监听都是在同一thread处理,不会造成太多性能开销。

6、处理时避免事件循环操作。比如监听到open事件在处理这个事件过程中又触发open事件点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值