我们总有这样的担忧:总有刁民想害朕,总有人偷偷在目录下删改文件,高危操作想第一时间了解,怎么办? 而且通常我们还有这样的需求:
怎么解决呢?通常来说有三个办法:
很显然第三种相对来说是最好的,具体怎么操作呢?
package main
import (
"github.com/fsnotify/fsnotify"
log "github.com/sirupsen/logrus"
)
func TestFileMonitor() {
// 创建文件/目录监听器
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal("watcher new err: [%+v]", err)
return
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
// 打印监听事件
log.Print("event:", event)
// 可以启动新的goroutine或使用channel进行事件传递
case _, ok := <-watcher.Errors:
if !ok {
return
}
}
}
}()
// 监听当前目录
err = watcher.Add("./")
if err != nil {
log.Fatal("err add dir: [%+v]", err)
}
<-done
}
我们来测试一下,先把上述程序编译,然后跑起来:
然后我们来创建一个文件:
使用 vim 打开这个文件,写入一行数据,然后关闭退出:
最终运行结果:
fsnotify 是跨平台的实现,这里只讲 Linux 平台的实现机制。fsnotify 本质上就是对系统能力的一个浅层封装,主要封装了操作系统提供的两个机制:
// fs/notify/inotify/inotify_user.c
// 创建 notify fd
inotify_init1()
// 添加监控路径
inotify_add_watch()
// 删除一个监控
inotify_rm_watch()
// 用法非常简单,分别对应 inotify fd 的创建,监控的添加和删除。
系统调用 -> vfs -> 具体文件系统( ext4 )-> 块层 -> scsi 层
(当然是 vfs 层,这是必然的,因为这是所有“文件”操作的入口。
)
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{
// ...
ret = __vfs_read(file, buf, count, pos);
if (ret > 0) {
// 事件采集点:访问事件
fsnotify_access(file);
}
}
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
// ...
ret = __vfs_write(file, buf, count, pos);
if (ret > 0) {
// 事件采集点:修改事件
fsnotify_modify(file);
}
}
static inline void fsnotify_modify(struct file *file)
{
// 获取到 inode
if (!(file->f_mode & FMODE_NONOTIFY)) {
fsnotify_parent(path, NULL, mask);
// 采集事件,通知到指定结构
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
}
}
fsnotify
-> send_to_group
-> inotify_handle_event
-> fsnotify_add_event
-> wake_up (唤醒等待队列,也就是 epoll)
// 把事件通知到相应的 group 上;
int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is, const unsigned char *file_name, u32 cookie)
{
// ...
// 把事件通知给正在监听的 fsnotify_group
while (fsnotify_iter_select_report_types(&iter_info)) {
ret = send_to_group(to_tell, mask, data, data_is, cookie, file_name, &iter_info);
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;
fsnotify_iter_next(&iter_info);
}
out:
return ret;
}
static int send_to_group(struct inode *to_tell, __u32 mask, const void *data, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_iter_info *iter_info)
{
// 通知相应的 group ,有事来了!
return group->ops->handle_event(group, to_tell, mask, data, data_is, file_name, cookie, iter_info);
}
// group->ops->handle_event 被赋值为 inotify_handle_event
int inotify_handle_event(struct fsnotify_group *group, struct inode *inode, u32 mask, const void *data, int data_type, const unsigned char *file_name, u32 cookie, struct fsnotify_iter_info *iter_info)
{
// 唤醒事件,通知相应的 group
ret = fsnotify_add_event(group, fsn_event, inotify_merge);
}
// 添加事件到 group
int fsnotify_add_event(struct fsnotify_group *group, struct fsnotify_event *event, int (*merge)(struct list_head *, struct fsnotify_event *))
{
// 唤醒这个等待队列
wake_up(&group->notification_waitq);
}
来看一眼结构体的简要关系:
关注我看更多有意思的文章哦!