了解一个接口,最好的方式莫过于亲手去测试,所以直接上示例代码:
代码来自 kqueue - NetBSD System Calls Manual
这段代码的主要功能是,监控一个指定文件,并打印出收到的事件消息。(文件由程序的第一个运行参数指定)
monitor.h
#include#include #include #include #include #include #include #include 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 就行。
参考文档:
1。kqueue - NetBSD System Calls Manual