调用 kqueue 接口的示例代码
- 博客分类:
- Python
了解一个接口,最好的方式莫过于亲手去测试,所以直接上示例代码:
代码来自 kqueue - NetBSD System Calls Manual
这段代码的主要功能是,监控一个指定文件,并打印出收到的事件消息。(文件由程序的第一个运行参数指定)
monitor.h
- #include <sys/types.h>
- #include <sys/event.h>
- #include <sys/time.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <err.h>
- int main(int argc, char *argv[])
- {
- int fd, kq, nev;
- struct kevent ev;
- static const struct timespec tout = { 1, 0 };
- if ((fd = open(argv[1], O_RDONLY)) == -1)
- err(1, "Cannot open `%s'", argv[1]);
- if ((kq = kqueue()) == -1)
- err(1, "Cannot create kqueue");
- EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
- NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK|
- NOTE_RENAME|NOTE_REVOKE, 0, 0);
- if (kevent(kq, &ev, 1, NULL, 0, &tout) == -1)
- err(1, "kevent");
- for (;;) {
- nev = kevent(kq, NULL, 0, &ev, 1, &tout);
- if (nev == -1)
- err(1, "kevent");
- if (nev == 0)
- continue;
- if (ev.fflags & NOTE_DELETE) {
- printf("deleted ");
- ev.fflags &= ~NOTE_DELETE;
- }
- if (ev.fflags & NOTE_WRITE) {
- printf("written ");
- ev.fflags &= ~NOTE_WRITE;
- }
- if (ev.fflags & NOTE_EXTEND) {
- printf("extended ");
- ev.fflags &= ~NOTE_EXTEND;
- }
- if (ev.fflags & NOTE_ATTRIB) {
- printf("chmod/chown/utimes ");
- ev.fflags &= ~NOTE_ATTRIB;
- }
- if (ev.fflags & NOTE_LINK) {
- printf("hardlinked ");
- ev.fflags &= ~NOTE_LINK;
- }
- if (ev.fflags & NOTE_RENAME) {
- printf("renamed ");
- ev.fflags &= ~NOTE_RENAME;
- }
- if (ev.fflags & NOTE_REVOKE) {
- printf("revoked ");
- ev.fflags &= ~NOTE_REVOKE;
- }
- printf("\n");
- if (ev.fflags)
- warnx("unknown event 0x%x\n", ev.fflags);
- }
- }
编译,并生成一个用于测试的待监控文件, 然后运行程序。
- cc monitor.c -o monitor
- touch zhongwei.log
- ./monitor zhongwei.log
对该文件进行各种操作,观察输出:
- echo "Hello kqueue" >> zhongwei.log
- # 输出为:
- # extended
- # written
- touch zhongwei.log
- # 输出为:
- # chmod/chown/utimes
- mv zhongwei.log zhongwei2.log
- # 输出为:
- # renamed
- rm zhongwei2.log
- # 输出为:
- # deleted hardlinked
监控效果很好,很强大。看一下代码:
- struct kevent ev;
在该示例中,结构体 kevent 用来描述待监测文件(例如,对应的 file descriptor, 需要监控的事件)。
- struct kevent {
- uintptr_t ident; /* identifier for this event */
- uint32_t filter; /* filter for event */
- uint32_t flags; /* action flags for kqueue */
- uint32_t fflags; /* filter flag value */
- int64_t data; /* filter data value */
- intptr_t udata; /* opaque user data identifier */
- };
结构体初始化操作呢?原来在这里:
- EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
- NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK|
- NOTE_RENAME|NOTE_REVOKE, 0, 0);
EV_SET 是一个宏定义,用于简化 kevent 结构体的初始化代码。
在 event.h 中可以看到 EV_SET 的定义,这个文件位于 (系统:Mac OS X 10.6.8)
- $ find /System/ -name event.h
- /System//Library/Frameworks/Kernel.framework/Versions/A/Headers/sys/event.h
- #define EV_SET(kevp, a, b, c, d, e, f) do { \
- struct kevent *__kevp__ = (kevp); \
- __kevp__->ident = (a); \
- __kevp__->filter = (b); \
- __kevp__->flags = (c); \
- __kevp__->fflags = (d); \
- __kevp__->data = (e); \
- __kevp__->udata = (f); \
- } while(0)
现在需要仔细看一下,结构体 kevent 各成员的含义 (括号内为示例代码中传入的实际参数):
ident (fd)
事件的标识,通常使用 file descriptor 来标识。
其他值还有 EVFILT_AIO, EVFILT_SIGNAL 等。
filter (EVFILT_VNODE)
指定用来处理该事件的 kernel filter. 通常是系统预定义的 kernel filter.
EVFILT_VNODE 说明要监控一个文件,具体需要监控的事件类型在 fflags 中指定。
flags (EV_ADD | EV_ENABLE | EV_CLEAR)
Actions to perform on the event.
例如:EV_ADD 是将该事件加入 kqueue;
EV_ENABLE 是允许 kevent() 函数返回该事件,当其触发时。
fflags (NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK|NOTE_RENAME|NOTE_REVOKE)
Filter-specific flags.
data (0)
Filter-specific data value.
udata (0)
Opaque user-defined value passed through the kernel unchanged.
详细信息查看 man kqueue 就行。
参考文档: