文件深度监控策略

背景

    最近很多项目组报文件丢失,文件被篡改等bug单子。所以想做个文件监控方案,可以监控某些目录下面或者文件的异常操作,做到能及时找到文件异常操作的元凶和异常操作的时间点,并及时输出log日志。

    这样如果推到用户那边,用户只需要定制一些监控事件,push到sdcard根目录下面,然后开启监控,就可以以后不用受开关机影响,永久的持续性的监控文件异常操作了。

方案设计

     这种监控方案设计时,无非需要考虑以下几点:

     1:是否影响手机性能  

     2:通用性

     3:出色完成功能

     4:操作方便

A: 是否影响手机性能

     我觉得这个是首要的。

     可以拿目前的 文件丢失 问题场景为例,该问题在用户那边复现概率低,而且随机性大,就是用户那边发生的文件丢失场景千变万化,而且导致文件丢失的因素很广泛,排查起来比较困难,手机上很多模块都逃脱不了干系。

     所以需要有一种方案,部署到用户那边,一旦开启监控后,能够持久性,长时间的运行,从而尽可能的找到文件异常操作原因。如果长时间的运行,那么用户肯定不能容忍影响到手机性能,比如手机开启监控后,文件访问变慢。

    该方案设计时,首先考虑了这一点,选择了inotify 作为监控方案的核心实施者。

    inotify在这方面的特点:

      1:用户态监控进程,没有监控事件到来时,是睡眠状态。有监控事件时,内核可以立刻唤醒该监控进程,从而让监控进程输出异常操作事件。

      2:内核态事件监听方面的处理,采用的是inode节点标记的行为。

          inotify需要用户指定一些需要监控的目录或者文件,还有需要监控的事件(删除or 篡改)。指定好后,这些目录或文件对应的iNode节点都会被打上inotify标签。从而当内核文件系统模块在对inode节点进行操作时,会看该节点有无被打上inotify标签。

          如果被打上标签了,并且该inode节点操作是用户指定要监控的事件时,这个时候,inotify架构才会输出log信息,最终唤醒用户态监控进程去处理。

    性能影响分析:    

   1 用户态:

   从上面第一点看出:要监控的事件不发生时,用户态监控进程一直是睡眠状态,除非事件发生。所以用户态监控进程对性能影响很小。

   2 内核态:

   由于inotify 代码已经和文件系统模块代码紧密结合到了一起,就算不加我的监控机制changes或者说加了监控机制changes,未开启监控时,内核文件系统正常运行,也会走到fsnotify 里面(详见下面的解决不足1 分析)。

    开启监控时,仅仅会对用户指定监控的文件去判断监控事件是否发生,发生后输出log记录。用户不指定的文件是不会去做判断的。

    所以对于用户没有指定监控的文件,性能还是跟未开启监控时候一样。  所以内核态该监控机制 对系统性能影响也不大。

     虽然采用inotify监控,内核态也需要做下额外的工作(判断监控事件是否发生和监控处理过程),但是权衡了手机性能和 监控功能的出色完成,这里觉得还是采用inotify比较好。

B: 通用性

    这个是指一个监控方案,可以用户随意定制监控事件,监控目录。不仅能够监控文件丢失,而且还能监控文件被acess,modify,move in, move out。从该方面考虑,inotify 正是合适的候选者。

C: 出色完成功能

    这个方面,原生的inotify 监控 有3点不足:

    1: inotify 只能汇报需要监控的事件发生了,但是不能找出哪个进程进行的异常操作,并且也没记录异常事件发生的时间点。

    2: inotify 用来监控目录里面发生的事件时,不能递归监控该目录里面动态生成的子目录。

    3:异常事件发生时,不能生成详细的log,供开发人员分析。

     所以对inotify 进行了改造。

  解决不足1:

        需要首先理清内核态inotify 代码架构。上面讨论inotify是否 影响手机性能时,已经说了inotify采用的是inode节点标记的方法。

        还是以具体的代码为例,讨论解决思路。

        inotify 内核态代码分前端和后端处理。

       前端是 文件系统各个inode节点的操作,如unlink, rmdir,  write, read. 后端是发生这些操作时,在合适的时机调用inotify模块进行监控事件的输出。

       以unlink为例:

        unlinkat → do_unlinkat → vfs_unlink2 → d_delete

        fs/dcache.c 里面d_delete:

void d_delete(struct dentry * dentry)

{

    .......

    if (dentry->d_lockref.count == 1) {

     .....

       dentry_unlink_inode(dentry);  

       fsnotify_nameremove(dentry, isdir);  // 开始从前端转向后端inotify模块处理

       return;

   }

   .....

   spin_unlock(&dentry->d_lock);

   fsnotify_nameremove(dentry, isdir);   // 开始从前端转向后端inotify模块处理

 }

      include/linux/fsnotify.h里面:

     fsnotify_nameremove → fsnotify_parent → fsnotify → send_to_group → group->ops->handle_event

fs/notify/inotify/inotify_fsnotify.c里面:

const struct fsnotify_ops inotify_fsnotify_ops = {

.handle_event = inotify_handle_event,   // 这个就是上面调用group->ops->handle_event里面的handl_event。

.free_group_priv = inotify_free_group_priv,

.free_event = inotify_free_event,

.freeing_mark = inotify_freeing_mark,

};

int inotify_handle_event(struct fsnotify_group *group,

struct inode *inode,

struct fsnotify_mark *inode_mark,

struct fsnotify_mark *vfsmount_mark,

u32 mask, void *data, int data_type,

const unsigned char *file_name, u32 cookie)

{

   ........ 

      fsn_event = &event->fse;

        fsnotify_init_event(fsn_event, inode, mask);

        event->wd = i_mark->wd;

        event->sync_cookie = cookie;

        event->name_len = len;   //产生本次监控事件log记录

   .......

     ret = fsnotify_add_event(group, fsn_event, inotify_merge);  //该函数会去唤醒用户态睡眠的那个监控进程,把本次监控事件记录传给该进程。

   ......

}

从上面整个代码流程来看,用户态的进程或线程执行流 会最终走到 inotify_handle_event里面,为了解决不足1, 可以在 上面产生监控事件记录的地方再加些代码,因为inotify_handle_event的调用依然属于前端inode节点操作的进程上下文。在这里加些代码,就获取到当前进程的Pid,name, group process leader pid and name,还有 异常操作发生的时间。

  解决不足2:

这个就是需要改造external/toybxo/toys/other/inotifyd.c 文件,具体在循环读内核态上报来的监控事件时,加入处理如下:

 如果上报上来的是子目录增加事件时,就调用 inotify_add_watch 增加对该子目录的监控。

这样通过解决不足1,2,inotify的功能 就强大起来,可以在异常操作发生时,及时输出异常操作的进程pid, name,还有该进程所在的进程组leader的pid,name,还有 异常操作时间。通过这些log输出,我们就大概找出进行异常操作的元凶了。

  解决不足3:

  虽然inotify已经可以找出异常操作的进程名字,pid了。但是有时候,光有这些信息还不足以提供有效的信息供开发找出本次异常操作的根本原因。

  所以还提供了异常监控事件发生时,能够输出logcat日志,这样对照inotify输出 的异常操作的进程名字,异常操作时间点,就可以通过log快速 定位 出根本原因了。

  但是logcat 输出 这里有个问题。

   比如监控某个文件的删除,由于无法区分正常情况下的删除和异常删除,所以无论正常还是异常,都会输出logcat。这样的话,输出的logcat文件数量就会变多。

   这里针对照片丢失场景,对这个问题暂时做了这样的优化:

       每隔十分钟,判断下是否有新的要监控的事件发生,如果发生了,输出份logcat。

     理由:

      1:针对照片丢失场景,正常情况下删除照片的操作也不会太频繁。用户删除照片,一般是做照片整理。这样以十分钟为间隔来判断的话,能够筛选出一个整理工作后,输出 一份logcat。

         这样输出的logcat 份数就不多了。

     2:异常操作情况下的删除,由于已经准确记录到了inotify 输出的log文件(这个文件跟logcat文件是两份独立的文件),所以离异常操作过去有十几分钟时间了,这段事件,logcat里面的log应该不会被冲掉。

          所以只要过个十几分钟,输出下logcat就行了。到时候对照下inotify输出的文件里面的删除时间点,删除进程名字看下就行了。

 D: 操作方便

  1:首先设计了手机暗码操作流程,通过暗码可以开启或者关闭监控。并且手机再次开机后,之前的监控状态依然能够保持。

   2: 最方便,最有效的操作是实现云端打开监控,云端传递监控事件,鉴于开发时间的不足,目前除了实现手机暗码操作监控外,还需要用户push 一个监控文件 到手机sdcard根目录下面。

该监控文件内容如下(以照片监控为例):

data/media/0/XXX/Gallery/cloud/   d

/data/media/0/DCIM/Camera/   m

其实就是列出了需要监控的目录或者文件,和需要监控的事件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值