kqueue使用
网上搜了下关于kqueue的使用,感觉都不如意,自己翻译了一下,放在git上。
英文手册: https://www.freebsd.org/cgi/man.cgi?query=kqueue&apropos=0&sektion=0&manpath=FreeBSD+12.2-RELEASE+and+Ports&arch=default&format=html
三个接口:
int kqueue(void);
kqueue()创建一个新的内核事件队列,返回一个文件描述符。如果有错误,将返回-1,并设置error。
int kevent(
int kq,
const struct kevent *changelist,
int nchanges,
struct kevent *eventlist,
int nevents,
const struct timespec *timeout
);
参数changlist、eventlist都是指向kevent结构的指针,changelist是要监听的事件,如果事件发生,会放在eventlist里。
函数返回放在eventlist里事件的数量,即放了多少个事件到eventlist。
其中有个比较重要的设定,如果nevents值是0,那kevent()会立即返回;如果nevents不为0,且timeout指针为空,那么kevent()会永久阻塞,直到事件发生。
EV_SET(kev, ident, filter, flags, fflags, data, udata);
// kevent结构
struct kevent {
uintptr_t ident; /* identifier for this event */
short filter; /* filter for event */
u_short flags; /* action flags for kqueue */
u_int fflags; /* filter flag value */
int64_t data; /* filter data value */
void *udata; /* opaque user data identifier */
uint64_t ext[4]; /* extensions */
};
宏EV_SE用来初始化kevent结构体。
ident:一般是文件描述符,比如socket、文件等。
filter:不知道怎么翻译,过滤器?
flags:对这个kevent,对kqueue进行怎样操作,是添加到kqueue队列里监听,还是从kqueue队列里移除,需要指明。
fflags:filter-flags,不同的filter有不同的fflags定义,得看是用的哪个filter。
其他的参数用的时候再查。
手册的kqueue例子
#include <sys/event.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
struct kevent event; /* Event we want to monitor */
struct kevent tevent; /* Event triggered */
int kq, fd, ret;
if (argc != 2)
err(EXIT_FAILURE, "Usage: %s path\n", argv[0]);
// 打开文件,拿到文件描述符
fd = open(argv[1], O_RDONLY);
if (fd == -1)
err(EXIT_FAILURE, "Failed to open '%s'", argv[1]);
/* Create kqueue. */
// 创建kqueue队列,返回描述符
kq = kqueue();
if (kq == -1)
err(EXIT_FAILURE, "kqueue() failed");
// EV_SET(kev, ident, filter, flags, fflags, data, udata);
/*
初始化kevent结构体
ident:为文件描述符
EVFILE_VNODE: 用这个filter
EV_ADD:添加到kqueue
EV_CLEAR:每次事件被取走,状态重置
NOTE_WRITE:每当ident指向的文件描述符有写入时返回
不用太纠结为什么要用EVFILE_VNODE这个filter,按照官网来说,这个filter就是要用监听文件变化的。
*/
EV_SET(&event, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_WRITE, 0, NULL);
/* Attach event to the kqueue. */
// 还记得前面的设定么?nevents为0,立即返回,返回的值是kqueue放到eventlist里的事件数量,这里eventlist为NULL,所以返回的ret是0。
// 所以这个语句的作用是向kqueue注册要监听的事件,仅此而已
ret = kevent(kq, &event, 1, NULL, 0, NULL);
if (ret == -1) // 注册失败会返回-1
err(EXIT_FAILURE, "kevent register");
if (event.flags & EV_ERROR) // 有其他错误,会置flags的EV_RROR位为1,错误数据放在data字段
errx(EXIT_FAILURE, "Event error: %s", strerror(event.data));
// 开启循环
for (;;) {
/* Sleep until something happens. */
// 这里nevents不为0,eventlist为这NULL,且timeout为空指针,那会永久阻塞,直到有事件产生
ret = kevent(kq, NULL, 0, &tevent, 1, NULL);
if (ret == -1) {
err(EXIT_FAILURE, "kevent wait");
} else if (ret > 0) {
// 每当有东西写到文件里了,就会触发事件
printf("Something was written in '%s'\n", argv[1]);
}
}
}
保存为test.c文件,编译:
gcc test.c o test
新建一个文件:
touch 1.txt
执行一下:
./test 1.txt
测试一下往1.txt写点东西:
echo "ewfwf " > 1.txt
会看打印 Something was written in '1.txt'。