[a] 概述

  • kqueue API 由两个函数(kqueue、kevent)、一个辅助宏(EV_SET)、一个结构体(struct kevent)构成,可以应用于 socket、FIFO、pipe、aio、signal、process、regular file、path 等对象
  • 与 Linux 下的 epoll 功能类似,性能相当

[b] kqueue / kevent / EV_SET

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
int kqueue(void)
//成功返回 kqueue 标识符,供 kevent 函数首个参数使用,出错返回 -1
int kevent(int kq, const struct kevent *changelist, int nchange, struct kevent *eventlist, int nevent, const struct timespec *timeout)
//成功返回 eventlist 数组的元素个数,出错信息写入 struct kevent 中的 flag 字段,不能写入的出错信息返回 -1,若 timeout 超时,则返回 0
EV_SET(kev, ident, filter, flags, fflags, data, udata)
//kev 是指向 struct kevent 的指针,其余字段依次对应于 struct kevent 中的字段 
struct kevent {
uintptr_t ident; //event 标识符,根据 filter 的不同可以为 fd、pid 等
short filter; //监听类别(目标),如 socket、process 等
u_short flags; //通用标志,可用于保存返回信息
u_int fflags; //特定 filter 的专有标志,可用于保存专有返回信息
intptr_t data; //特定 filter 存储专有信息
void *udata; //进程预定义内容,常作识别用途,kqueue 不更改此项,原样返回其内容
  • kqueue 函数用于初始化一个 kqueue 队列,单个进程可以建立多个 kqueue,kqueue 描述符与文件描述符类似,也可以复用,不能被 fork 出的子进程继承
  • kevent 用于设置策略及如何获取结果,其中:
    • changelist 用于添加或修改 event
    • eventlist 用于向调用进程返回 event 结果,若实际产生的事件数量超过了 nevent,可通过多次调用获取全部结果;若 nevent 为 0,则即使指定了 timeout 也会立即返回,不阻塞
    • changelist 与 eventlist 可以指向同一个 kevent 结构体数组;nchange 与 nevent 分别代表两个结构体数组的元素数量上限
    • 如果 timeout 参数为 NULL,则 kevent 函数将阻塞,直到有事件发生,若不为 NULL,阻塞到超时为止(<24h),若 timespec 结构体各字段均设置为 0,则立即返回当前尚未处理的所有事件
  • EV_SET 仅是预定义的一个用于设置 struct kevent 的辅助宏,与手动指定结构体的各字段效果相同

[c] filter 及专有规则

  • /usr/include/sys/event.h 中定义的 10 种 filter
  • #define EVFILT_READ             (-1)
    #define EVFILT_WRITE (-2)
    #define EVFILT_AIO (-3) /* attached to aio requests */
    #define EVFILT_VNODE (-4) /* attached to vnodes */
    #define EVFILT_PROC (-5) /* attached to struct proc */
    #define EVFILT_SIGNAL (-6) /* attached to struct proc */
    #define EVFILT_TIMER (-7) /* timers */
    #define EVFILT_PROCDESC (-8) /* attached to process descriptors */
    #define EVFILT_FS (-9) /* filesystem events */
    #define EVFILT_LIO (-10) /* attached to lio requests */
  • EVFILT_READ:用于检测数据何时可读,不同的描述符将返回不同的信息
    • 若 ident 字段指定为一个文件描述符,则该事件表示文件偏移量尚未到达文件末尾,当前基于 SEEK_END 的 offset 值保存在 data 成员字段中
    • 若为 pipe 或 FIFO 则表示已有实际数据可读,data 字段存储可读的字节数量
  • EVFILT_WRITE:用于检测是否可以对描述符执行写操作;对 pipe、FIFO 或 socket,data 字段代表缓冲区中可用的空间(bytes),此 filter 对文件或目录没有意义(vnodes)
  • EVFILT_AIO:用于异步 I/O 操作,用于检测和 aio_error 系统调用类似的条件
  • EVFILT_VNODE:用于检测文件系统上某个文件的各种变动事件,ident 须指定一个有效的文件描述符,fflags 字段设定检测的事件类别(如下)
    • #define NOTE_DELETE     0x0001                  /* vnode was removed */
      #define NOTE_WRITE 0x0002 /* data contents changed, a write occurred on the file */
      #define NOTE_EXTEND 0x0004 /* size increased */
      #define NOTE_ATTRIB 0x0008 /* attributes changed */
      #define NOTE_LINK 0x0010 /* link count changed */
      #define NOTE_RENAME 0x0020 /* vnode was renamed */
      #define NOTE_REVOKE 0x0040 /* vnode access was revoked */
      #define NOTE_OPEN 0x0080 /* vnode was opened */
      #define NOTE_CLOSE 0x0100 /* file closed, fd did not allowed write */
      #define NOTE_READ 0x0400 /* file was read */
  • EVFILT_PROC:用于检测发生在另一个进程里的事件,此时 ident 字段须指定进程 ID,fflags 字段设定检测的事件类别(如下)
    • #define NOTE_EXIT       0x80000000              /* process exited, exit status will be stored in 'data' */
      #define NOTE_FORK 0x40000000 /* process forked */
      #define NOTE_EXEC 0x20000000 /* process exec'd */
      #define NOTE_TRACK 0x00000001 /* follow across forks */
      #define NOTE_TRACKERR 0x00000002 /* could not track child */
      #define NOTE_CHILD 0x00000004 /* am a child process */
  • EVFILT_SIGNAL:检测是否有信号发送至 ident 所指定的进程 ID
    • 事件将在进行完常规的信号处理之后,放到 kqueue 中,data 字段存储收到的信号数量,包括被设置了 SIG_IGN 的信号(SIG_CHLD 除外,此信号若被忽略则不计数)
    • 此 filter 默认带有 EV_CLEAR 标志

[d] 通用标志:flags

  • /usr/include/sys/event.h 定义的通用 flags,按位 OR 进行组合

    • /* actions */
      #define EV_ADD 0x0001 /* add event to kq (implies enable) */
      #define EV_DELETE 0x0002 /* delete event from kq */
      #define EV_ENABLE 0x0004 /* enable event */
      #define EV_DISABLE 0x0008 /* disable event (not reported) */
      #define EV_FORCEONESHOT 0x0100 /* enable _ONESHOT and force trigger */ /* flags */
      #define EV_ONESHOT 0x0010 /* only report one occurrence */
      #define EV_CLEAR 0x0020 /* clear event state after reporting */
      #define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
      #define EV_DISPATCH 0x0080 /* disable event after once reporting */ #define EV_SYSFLAGS 0xF000 /* reserved by system */
      #define EV_DROP 0x1000 /* note should be dropped */
      #define EV_FLAG1 0x2000 /* filter-specific flag */
      #define EV_FLAG2 0x4000 /* filter-specific flag */ /* returned values */
      #define EV_EOF 0x8000 /* EOF detected */
      #define EV_ERROR 0x4000 /* error, data contains errno */
  • EV_ADD:添加事件,默认附带 EV_ENABLE 行为,相同事件重复添加会导致旧的事件被覆盖
  • EV_DELETE:从 kqueue 中清除指定项(filter)
  • EV_DISABLE:禁止 kqueue 返回某个事件的信息,但并不删除
  • EV_ONESHOT:针对同一对象的同类事件仅返回一次
  • EV_CLEAR:返回事件后清空现有状态,用途是监测每次状态的改变,而非当前的状态;对可写、可读这种有持续存在的事件状态往往需要设置,对一次性状态,如信号计数,则不需要

[e] 样例

#include <sys/types.h>
#include "sys/event.h"
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h> char *t0 = "/tmp/test_0";
char *t1 = "/tmp/test_1"; void unlinktmp(int signo)
{
unlink(t0);
unlink(t1);
printf("%s, %s deleted! \n", t0, t1);
exit();
} int main(void)
{
struct kevent event[];
struct kevent tevent[];
int kq, ret;
unsigned long fd_0, fd_1; struct sigaction act;
act.sa_handler=unlinktmp;
sigemptyset(&act.sa_mask);
act.sa_flags=; sigaction(SIGINT, &act, NULL); fd_0 = open(t0, O_WRONLY|O_CREAT|O_TRUNC, );
fd_1 = open(t1, O_WRONLY|O_CREAT|O_TRUNC, ); kq = kqueue();
if (kq == -)
{
perror("kqueue()");
} EV_SET(&event[], fd_0, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_LINK|NOTE_EXTEND, , NULL);
EV_SET(&event[], fd_1, EVFILT_VNODE, EV_ADD|EV_CLEAR, NOTE_LINK|NOTE_EXTEND, , NULL); kevent(kq, event, , NULL, , NULL); while()
{
ret = kevent(kq, NULL, , tevent, , NULL);
if (ret < )
{
perror("kevent");
}
else
{
for(int i = ; i < ret; ++i)
{
if (tevent[i].fflags & NOTE_LINK)
{
printf("%s: link num changed! \n", tevent[i].ident == fd_0 ? t0 : t1);
}
else if (tevent[i].fflags & NOTE_EXTEND)
{
printf("%s: extanded! \n", tevent[i].ident == fd_0 ? t0 : t1);
}
else
{
printf("%s: nothing happened!\n", tevent[i].ident == fd_0 ? t0 : t1);
}
}
}
}
}

[13]APUE:KQUEUE / FreeBSD的更多相关文章

  1. Windows Phone 8初学者开发—第13部分:设置LongListSelector中磁贴的样式

    原文 Windows Phone 8初学者开发—第13部分:设置LongListSelector中磁贴的样式 第13部分:设置LongListSelector中磁贴的样式 原文地址: http://c ...

  2. Windows Phone开发(13):如何规范用户的输入行为

    原文:Windows Phone开发(13):如何规范用户的输入行为 很多时候,我们对用户的操作或输入做一定程度的限制,以避免发生不必要的异常或错误,因此,对一些特殊的类型,进行输入限制是很有必要的. ...

  3. JVM基础系列第13讲:JVM参数之追踪类信息

    我们都知道 JVM 在启动的时候会去加载类信息,那么我们怎么得知他加载了哪些类,又卸载了哪些类呢?我们这一节就来介绍四个 JVM 参数,使用它们我们就可以清晰地知道 JVM 的类加载信息. 为了方便演 ...

  4. Qt 学习之路 2(13):对话框简介

    Qt 学习之路 2(13):对话框简介  豆子  2012年9月14日  Qt 学习之路 2  53条评论 对话框是 GUI 程序中不可或缺的组成部分.很多不能或者不适合放入主窗口的功能组件都必须放在 ...

  5. 《Cracking the Coding Interview》——第13章:C和C++——题目1

    2014-04-25 19:13 题目:用C++写一个读取文件倒数K行的方法. 解法:因为是要取倒数K行,所以我的思路是一行一行地读.过程中需要保存一个长度为K的链表,每次新读到一行都将表头节点移到表 ...

  6. Android零基础入门第13节:Android Studio配置优化,打造开发利器

    原文:Android零基础入门第13节:Android Studio配置优化,打造开发利器 是不是很多同学已经有烦恼出现了?电脑配置已经很高了,但是每次运行Android程序的时候就很卡,而且每次安装 ...

  7. 小白学 Python(13):基础数据结构(字典)(下)

    人生苦短,我选Python 前文传送门 小白学 Python(1):开篇 小白学 Python(2):基础数据类型(上) 小白学 Python(3):基础数据类型(下) 小白学 Python(4):变 ...

  8. 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】

    原文:循序渐进学.Net Core Web Api开发系列[13]:中间件(Middleware) 系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:ht ...

  9. 13 | 实战:单机如何实现管理百万主机的心跳服务? https://time.geekbang.org/column/article/240656

    13 | 实战:单机如何实现管理百万主机的心跳服务? https://time.geekbang.org/column/article/240656

随机推荐

  1. amqp事务

    摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事务以及发布确认是针对channel来讲,所以在一个连接中两个channel,一个channel可以 ...

  2. 【转】Android实现点击两次返回键退出

    在做安卓应用是我们经常要判断用户对返回键的操作,一般为了防止误操作都是在用户连续按下两次返回键的时候提示用户是否退出应用程序. 第一种实现的基本原理就是,当按下BACK键时,会被onKeyDown捕获 ...

  3. jsPlumb的简单使用

    jsPlumb概述jsPlumb是一个在dom元素之间绘制连接线的javascript框架,它使用svg技术绘制连接线. 基本概念很明显,一个连线主要要解决的问题包括谁和谁连,在哪里连(连接点在哪里) ...

  4. {Reship}{Code}{CV}

    UIUC的Jia-Bin Huang同学收集了很多计算机视觉方面的代码,链接如下: https://netfiles.uiuc.edu/jbhuang1/www/resources/vision/in ...

  5. 【转】 C# 小技巧之获取变量名称

    link: http://www.cnblogs.com/gongy/p/lm-2015-04-03.html 今天在自我规范程序设计的时候,变量名匹配字符串来自配置文件,网上找了一会儿发现也有朋友在 ...

  6. MongDB/C# 杂项

    1.MongDB的时间类型字段输出时为UTC的解决方法:保存到数据库中的数据还是按UTC存的,读出来的就按标识值读 [BsonDateTimeOptions(Kind = DateTimeKind.L ...

  7. C89和C99区别--简单总结

    (1)对数组的增强 可变长数组 C99中,程序员声明数组时,数组的维数可以由任一有效的整型表达式确定,包括只在运行时才能确定其值的表达式,这类数组就叫做可变长数组,但是只有局部数组才可以是变长的.可变 ...

  8. vim编辑二进制文件

    首先,vim -b 方式打开二进制文件, 然后用 :%!xxd去展示二进制文件 再修改文件, 最后用 :%!xxd -r去还原文件原来的展示方式, 并保存退出.

  9. find命令:忽略一个目录或者多个目录

    example: find ./ ! -path "./output/*" ! -path "./output1/*" -type f | xargs grep ...

  10. JavaScript 随笔2 面向对象 原型链 继承

    第六章 面向对象的程序设计 1.创建对象的几种方式 A)工厂模式 function CreatObj(name,sex,age){ this.name=name; this.sex=sex; this ...