Skip to content

2. BSD kqueue 编程:开始 [EVFILT_TIMER 为例]

[BSD kqueue 编程指南:目录]

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

这个例子来自于 NetBSD wiki 并做稍微的改变。

https://wiki.netbsd.org/tutorials/kqueue_tutorial

其功能简单明了,使用 EVFILT_TIMER 来模拟一个定时器每间隔一秒输出当前时间到字符终端。

EV_SET(kev, ident, filter, flags, fflags, data, udata);

• flags 是作用在事件上的行为,可以是

EV_ADD
EV_ENABLE
EV_DISABLE
EV_DISPATCH
EV_DELETE
EV_RECEIPT
EV_ONESHOT
EV_CLEAR
EV_EOF
EV_ERROR

• fflags 是跟 filter 相关的具体参数。在这个例子里 EV_SET 中设置 fflags 为 0,那么 data 默认为 milliseconds。

对于EVFILT_TIMER, fflags 可以是

NOTE_SECONDS - data 是秒 seconds
NOTE_MSECONDS - data 是 1/1000 秒 milliseconds
NOTE_USECONDS - data 是 1/1000000 秒 microseconds
NOTE_NSECONDS - data 是 1/1000000000 秒 nanoseconds

• filter 鉴定用于处理这个事件的内核过滤器类型,系统预定义的过滤器 filter 有这些:

EVFILT_READ
EVFILT_WRITE
EVFILT_AIO
EVFILT_VNODE
EVFILT_PROC
EVFILT_PROCDESC
EVFILT_SIGNAL
EVFILT_TIMER
EVFILT_USER

 

如果想阅读上面罗列的定义的具体内涵可以阅读kqueue 手册,在类 BSD 操作系统中敲入 man kqueue。

 

接下来用一个短小的例子说明 kqueue/kevent 运行的全过程,去掉注释真的非常非常短小了。

kqueue-hello-timer.cpp
//
// FreeBSD license
// Copyright 2017 zfs.cc. All rights reserved.
// https://www.freebsd.org/copyright/freebsd-license.html
// https://wiki.netbsd.org/tutorials/kqueue_tutorial
//

#include <string>
#include <iostream>
#include <cstring>        // strerror
#include <unistd.h>
#include <sys/event.h>    // kqueue
#include <sys/time.h>

void exit_as(const std::string & err) {
        std::cerr << "error: " << err << std::endl;
        std::exit(EXIT_FAILURE);
}

int main() {
        int kq = kqueue();      // 失败返回 -1, 成功返回文件描述符

        if (kq == -1)
                exit_as("kqueue()");

        struct kevent change, event;
        // 简单的来说,EV_SET 宏用于设置 struct kevent 结构体六个数据成员的值
        // 这里使用了定时器这个 filter, 定时 1000 毫秒
        EV_SET(&change, 0, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0, 1000, nullptr);

        while (true) {
                // kevent 系统调用用 kq 这个 queue 来注册 event, 最后一个参数设置超时,
                // 由于设置成 nullptr,那么 kevent 系统调用将无限等待直到得到通知,
                // 而先前用 EV_SET 设置了 data 为 1000 毫秒(1秒),1 秒后返回。
                // 失败返回 -1,成功返回事件的数量。
                int N = kevent(kq, &change, 1, &event, 1, nullptr);
                if (N == -1)
                        exit_as("kevent()");

                if (event.flags & EV_ERROR)
                        exit_as(std::strerror(event.data));

                // kevent 返回后就执行后续的代码,这里是创建子进程来输出当前时间

                // 因而代码的功能是每间隔 1 秒显示一次当前时间。

                // c++17 if
                if (pid_t pid = fork(); pid == -1)
                        exit_as("fork()");
                else if (pid == 0)
                        execlp("date", "date", nullptr);
        }
}

由于简单的使用使用了 c++17 新的 if (允许 if 先有一初始化语句再判断),所以在编译参数里需要加上 -std=c++17

[BSD kqueue 编程指南:目录]

 

 

引用

没有引用

回复

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

没有回复

新增回复

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

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

Form options

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