Skip to content

3. BSD kqueue - EVFILT_VNODE 监控文件变化

[BSD kqueue 编程指南:目录]

BSD kqueue 是类 BSD 操作系统中的内核事件通知机制,在所有的类 BSD 操作系统中都可以得到。类 BSD 操作系统包括 FreeBSD, NetBSD, OpenBSD 以及从这三大操作系统演化而来的各种分支。

这个例子来自于 kqueue 手册(在类 BSD 操作系统中敲入 man kqueue 开始阅读)并做稍微的改变。

这个例子用 EVFILT_VNODE 过滤器关联一个文件,当文件中被写入字符时 kevent 系统调用就会收到通知而返回。

跟 man 文档中原来的例子不同的是,创建了一个独立的线程往我们需要用  EVFILT_VNODE 监控的文件中每隔一秒写入一行字符作为测试,这样就不需要手动的往文件写东西了。

kqueue-vnode-NOTE_WRITE.cpp
//
// FreeBSD license
// Copyright 2017 zfs.cc. All rights reserved.
// https://www.freebsd.org/copyright/freebsd-license.html
// man kqueue
//

#include <string>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <sys/event.h>
#include <fcntl.h>
#include <future>
#include <mutex>
#include <chrono>

using namespace std::string_literals;
using namespace std::chrono_literals;

std::mutex mutex;

// c++17 inline 变量,跟 inline 函数具有相同的语义
inline auto isleep = [] (auto period) {
    std::this_thread::sleep_for(period);
};

void exitif(bool cond, const std::string & err) {
    if (cond) {
        std::unique_lock<std::mutex> guard(mutex);
        std::cerr << "error: " << err << std::endl;
        guard.unlock();
        std::exit(-1);
    }
}

auto writer = [] (const char * fn) {
    isleep(1s);
    while (true) {
        isleep(1s);
        std::unique_lock<std::mutex> guard(mutex);
        std::ofstream file(fn, std::ios::app);
        // exitif 中锁定了同一个 mutex,所以需要先解锁再调用 exitif,
        // 否则嵌套锁定造成死锁
        guard.unlock();
        exitif(!file.is_open(), "can not open file "s + fn);
        guard.lock();
        file << "leave a log." << std::endl;
        file.close();
        guard.unlock();
    }
};

int main() {
    constexpr const char * fn = "write.log";
    int fd = open(fn, O_CREAT | O_RDONLY);
    exitif(fd == -1, "fcntl open "s + fn + " error");
    // 不需要手动往文件里写入字符了,创建一个独立的线程每隔一秒往文件里写入数据
    auto f = std::async(std::launch::async, writer, fn);

    int kq = kqueue();
    exitif(kq == -1, "kqueue()");

    struct kevent change, event;
    EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, nullptr);
    int N = kevent(kq, &change, 1, nullptr, 0, nullptr);
    exitif(N == -1, "kevent()");
    exitif(change.flags & EV_ERROR, std::strerror(change.data));

    while (true) {
        // kevent system call will block until file written event happens
        int N = kevent(kq, nullptr, 0, &event, 1, nullptr);
        exitif(N == -1, "kevent() ...");
        if (N > 0) {
            std::unique_lock<std::mutex> guard(mutex);
            std::cout << "File "s + fn << " has been written." << std::endl;
        }
    }
}

由于使用了多线程,需要在编译链接参数里加上 pthread 链接标志,在 FreeBSD 里是 -lthr, 在 OpenBSD, NetBSD 里是 -pthread。

[BSD kqueue 编程指南:目录]

 

 

引用

没有引用

回复

回复显示方式 直线程 | 分线程

没有回复

新增回复

电子邮件地址将不会被显示,而仅将被用于发送电子邮件通知

为了阻止机器人提交垃圾回复,请在相应的文本框中输入你在下面的图片中所看到的字符串。只有在你输入的字符串和图片中的字符串吻合的情况下,你的回复才能被成功提交。请确认你的浏览器支持、并且已经开启了cookies功能,否则的话,你的回复无法被正确地验证。
CAPTCHA

Form options

发布的回复需要管理员审核