Post

使用inotify来监控系统文件

使用inotify来监控系统文件

1 前言

对于linux系统的文件,如果需要监控其文件状态是否出现了增、删、改,在2.6.13或更新的linux kenerl版本上,我们可以用linux系统中提供的inotifyapi 来实现

详细的api说明可以参考: man-pages

2 应用实现

2.1 主要逻辑

关键的函数有三个

1
2
3
inotify_init()
inotify_add_watch()
inotify_rm_watch()

我们直接从代码实现来看api的使用

1.初始化inotify并得到文件描述符fd

1
2
3
4
5
    int fd = inotify_init();
    if (fd == -1) {
        std::cerr << "Failed to initialize inotify" << std::endl;
        return;
    }

2.添加需要监控的文件/文件夹路径 得到监控文件描述符wd

1
2
3
4
5
6
7
8
#define INOTIFY_FOLDER_MASK (IN_CREATE | IN_MODIFY | IN_DELETE)
    std::string path = "/home/xx/"; // 可以是一个目录
    // std::string path = "/home/xx/test.txt" // 也可以是一个文件
    int wd = inotify_add_watch(fd, path.c_str(), INOTIFY_FOLDER_MASK);
    if (wd == -1) {
        std::cerr << "Failed to add watch to [" << path << "]" << ", error:" + std::to_string(errno) << std::endl;
        return;
    }

3.通过read等待inotify事件唤醒, 并处理inotify_event

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
        char buf[1024];
        int len = read(fd, buf, sizeof(buf));
        if (len == -1) {
            if (errno == EINTR) {
                std::cerr << "Interrupted by signal" << std::endl;
            }
            break;
        }
        std::cout << "read ok, len:" << len << std::endl;
        for (int i = 0; i < len;) {
            struct inotify_event *event = (struct inotify_event *)&buf[i];
            i += sizeof(struct inotify_event) + event->len;
            if (event->mask & IN_CREATE) {
                std::cout << "File created: " << event->name << std::endl;
            } else if (event->mask & IN_MODIFY) {
                std::cout << "File modified: " << event->name << std::endl;
            } else if (event->mask & IN_DELETE) {
                std::cout << "File deleted: " << event->name << std::endl;
            }
        }

4.inotify_rm_watch 移除监控

1
2
3
4
5
6
7
    if (fd == -1) {
        return;
    }
    if (wd != -1) {
        inotify_rm_watch(fd, wd);
    }
    close(fd);

2.2 注意事项

在实际场景中,我们通过step3拿到了inotify_event,如何判断这是否是我们监控的目标文件,这需要根据step2中监控的path本身是文件还是目录来针对性处理

2.2.1 inotify path为文件的处理

检查event中的wd是否和step2中拿到的一致

1
event->wd == wd

注意这种情况下拿到的event->name实际上是空的

2.2.2 inotify path为目录的处理

检查event->name

1
event->name == targetName // targetName即为你这个目录中你要看的文件名 例如test.txt

2.3 逻辑优化

实际业务中可以把这些处理封装到一个FileMonitor类当中,对于变动文件的后处理也可以作为std::functionalCallback灵活添加实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
enum MONI_TYPE {
    MONI_FILE,
    MONI_FOLDER,
    MONI_END,
};
class FileMonitor {
public:
    FileMonitor();
    FileMonitor(std::string str);
    ~FileMonitor();
    void monitor();
    void registerHandle(std::function<void(std::string)>);
    void setTargetFileName(std::string name);
    static void stopRun();
private:
    int fd;
    int wd;
    std::string path;
    std::string targetfileName;
    MONI_TYPE moniType;
    static bool run;
    void init();
    void exit();
    void checkType();
    void addDeviceInotify();
    bool match(std::string, int);
    std::string getRealPath();
    std::function<void(std::string)> callBack;
};

自定义Callback, 例如发现目标文件变动后我需要读取它

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void CallBack(std::string str) 
{
    std::string line;
    std::ifstream ifs(str);
    if (!ifs.good()) {
        std::cerr << "Failed to open file: " << str << std::endl;
        return;
    }
    // just read first for test
    if (std::getline(ifs, line)) {
        std::cout << "Read line: " << line << std::endl;
    }
}

int main()
{
    std::string pathStr = "/home/xx/";
    FileMonitor monitor(pathStr);
    monitor.registerHandle(CallBack);
}

文件变动后调用Callback

1
2
3
4
5
6
7
8
9
10
if (event->mask & IN_MODIFY) {
    std::cout << "File modified: " << event->name << std::endl;
    if (event->wd == wd) {
        if (callBack) {
            callBack(path);
        } else {
            std::cerr << "No handle function registered" << std::endl;
        }
    }
} 

一份笔者的完整的实现可以参考: FileMonitor

3 拓展

对于仅监控单个文件变动的场景,上述代码即可满足需求. 当然对于更复杂的场景我们可以结合另外的系统api和库来实现

3.1 监控多个文件变动并处理

linux 系统调用 inotify & epoll

3.2 监控文件变动并异步处理

利用 Boost.Asio 实现异步 inotify 事件监听

This post is licensed under CC BY 4.0 by the author.

Trending Tags