网络服务器通常都使用epoll进行异步IO处理,而开发者通常使用mac,为了方便开发,我把自己的handy库移植到了mac平台上。移植过程中,网上居然没有搜到kqueue的使用例子,让我惊讶不已。为了让大家不用像我一样再次花费大力气搞定kqueue,我整理了一个简单清晰可运行的kqueue例子,供大家参考。 
kqueue一共有几个函数:

 //类似epoll_create
int kqueue(void);
//兼具epoll_ctl及epoll_wait功能
int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);
//设定kevent参数的宏
EV_SET(&kev, ident, filter, flags, fflags, data, udata);
struct kevent {
uintptr_t ident; /* identifier for this event */
int16_t filter; /* filter for event */
uint16_t flags; /* general flags */
uint32_t fflags; /* filter-specific flags */
intptr_t data; /* filter-specific data */
void *udata; /* opaque user data identifier */
};

函数调用示例:

 //创建kqueue
int epollfd = kqueue();
//添加或者修改fd
struct kevent ev[];
int n = ;
if (events & kReadEvent) {
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, , , (void*)(intptr_t)fd);
}
if (events & kWriteEvent) {
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, , , (void*)(intptr_t)fd);
}
printf("%s fd %d events read %d write %d\n",
modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
int r = kevent(efd, ev, n, NULL, , NULL);
//获取ready的fd
struct timespec timeout;
timeout.tv_sec = waitms / ;
timeout.tv_nsec = (waitms % ) * * ;
const int kMaxEvents = ;
struct kevent activeEvs[kMaxEvents];
int n = kevent(efd, NULL, , activeEvs, kMaxEvents, &timeout);
//处理IO事件
for (int i = ; i < n; i ++) {
int fd = (int)(intptr_t)activeEvs[i].udata;
int events = activeEvs[i].filter;
if (events == EVFILT_READ) {
handleRead(efd, fd);
} else if (events == EVFILT_WRITE) {
handleWrite(efd, fd);
}
}

注意kevent与epoll最大的不同在于READ/WRITE事件是分开注册并且分开返回的,而Epoll则是一个fd一次返回读和写事件,用标志位来判断。 
可以运行的代码如下:kqueue-examplehandy对kqueue提供了封装版本)

 #include <sys/socket.h>
#include <sys/event.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h> #define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);} const int kReadEvent = ;
const int kWriteEvent = ; void setNonBlock(int fd) {
int flags = fcntl(fd, F_GETFL, );
exit_if(flags<, "fcntl failed");
int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
exit_if(r<, "fcntl failed");
} void updateEvents(int efd, int fd, int events, bool modify) {
struct kevent ev[];
int n = ;
if (events & kReadEvent) {
EV_SET(&ev[n++], fd, EVFILT_READ, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_READ, EV_DELETE, , , (void*)(intptr_t)fd);
}
if (events & kWriteEvent) {
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_ADD|EV_ENABLE, , , (void*)(intptr_t)fd);
} else if (modify){
EV_SET(&ev[n++], fd, EVFILT_WRITE, EV_DELETE, , , (void*)(intptr_t)fd);
}
printf("%s fd %d events read %d write %d\n",
modify ? "mod" : "add", fd, events & kReadEvent, events & kWriteEvent);
int r = kevent(efd, ev, n, NULL, , NULL);
exit_if(r, "kevent failed ");
} void handleAccept(int efd, int fd) {
struct sockaddr_in raddr;
socklen_t rsz = sizeof(raddr);
int cfd = accept(fd,(struct sockaddr *)&raddr,&rsz);
exit_if(cfd<, "accept failed");
sockaddr_in peer, local;
socklen_t alen = sizeof(peer);
int r = getpeername(cfd, (sockaddr*)&peer, &alen);
exit_if(r<, "getpeername failed");
printf("accept a connection from %s\n", inet_ntoa(raddr.sin_addr));
setNonBlock(cfd);
updateEvents(efd, cfd, kReadEvent|kWriteEvent, false);
} void handleRead(int efd, int fd) {
char buf[];
int n = ;
while ((n=::read(fd, buf, sizeof buf)) > ) {
printf("read %d bytes\n", n);
int r = ::write(fd, buf, n); //写出读取的数据
//实际应用中,写出数据可能会返回EAGAIN,此时应当监听可写事件,当可写时再把数据写出
exit_if(r<=, "write error");
}
if (n< && (errno == EAGAIN || errno == EWOULDBLOCK))
return;
exit_if(n<, "read error"); //实际应用中,n<0应当检查各类错误,如EINTR
printf("fd %d closed\n", fd);
close(fd);
} void handleWrite(int efd, int fd) {
//实际应用应当实现可写时写出数据,无数据可写才关闭可写事件
updateEvents(efd, fd, kReadEvent, true);
} void loop_once(int efd, int lfd, int waitms) {
struct timespec timeout;
timeout.tv_sec = waitms / ;
timeout.tv_nsec = (waitms % ) * * ;
const int kMaxEvents = ;
struct kevent activeEvs[kMaxEvents];
int n = kevent(efd, NULL, , activeEvs, kMaxEvents, &timeout);
printf("epoll_wait return %d\n", n);
for (int i = ; i < n; i ++) {
int fd = (int)(intptr_t)activeEvs[i].udata;
int events = activeEvs[i].filter;
if (events == EVFILT_READ) {
if (fd == lfd) {
handleAccept(efd, fd);
} else {
handleRead(efd, fd);
}
} else if (events == EVFILT_WRITE) {
handleWrite(efd, fd);
} else {
exit_if(, "unknown event");
}
}
} int main() {
short port = ;
int epollfd = kqueue();
exit_if(epollfd < , "epoll_create failed");
int listenfd = socket(AF_INET, SOCK_STREAM, );
exit_if(listenfd < , "socket failed");
struct sockaddr_in addr;
memset(&addr, , sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
int r = ::bind(listenfd,(struct sockaddr *)&addr, sizeof(struct sockaddr));
exit_if(r, "bind to 0.0.0.0:%d failed %d %s", port, errno, strerror(errno));
r = listen(listenfd, );
exit_if(r, "listen failed %d %s", errno, strerror(errno));
printf("fd %d listening at %d\n", listenfd, port);
setNonBlock(listenfd);
updateEvents(epollfd, listenfd, kReadEvent, false);
for (;;) { //实际应用应当注册信号处理函数,退出时清理资源
loop_once(epollfd, listenfd, );
}
return ;
}

kqueue例子的更多相关文章

  1. 可扩展的事件复用技术:epoll和kqueue

    通常来说我喜欢Linux更甚于BSD系统,但是我真的想在Linux上拥有BSD的kqueue功能. 什么是事件复用技术 假设你有一个简单的web服务器,并且那里已经打开了两个socket连接.当服务器 ...

  2. 再谈select, iocp, epoll,kqueue及各种I/O复用机制

    原文:http://blog.csdn.net/shallwake/article/details/5265287 首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonbl ...

  3. Kqueue与epoll机制

    首先介绍阻塞与非阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做):那么你可以去睡觉了,因为你知道快递把货送来 ...

  4. [转]Kqueue与epoll机制

    首先介绍阻塞与非阻塞:阻塞是个什么概念呢?比如某个时候你在等快递,但是你不知道快递什么时候过来,而且你没有别的事可以干(或者说接下来的事要等快递来了才能做):那么你可以去睡觉了,因为你知道快递把货送来 ...

  5. select, iocp, epoll,kqueue及各种I/O复用机制

    http://blog.csdn.net/heyan1853/article/details/6457362 首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblock ...

  6. 【网络】再谈select, iocp, epoll,kqueue及各种I/O复用机制 && Reactor与Proactor的概念

    首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...

  7. epoll 或者 kqueue 的原理是什么?

    来自知乎:http://www.zhihu.com/question/20122137 epoll 或者 kqueue 的原理是什么? 为什么epoll和kqueue可以用基于事件的方式,单线程的实现 ...

  8. 转: 再谈select, iocp, epoll,kqueue及各种I/O复用机制

    首先,介绍几种常见的I/O模型及其区别,如下: blocking I/O nonblocking I/O I/O multiplexing (select and poll) signal drive ...

  9. 高级IO模型之kqueue和epoll

    目录 简介 block IO和nonblocking IO IO多路复用和select poll epoll kqueue epoll和kqueue的优势 简介 任何一个程序都离不开IO,有些是很明显 ...

随机推荐

  1. .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理

    .NET Core中间件的注册和管道的构建(1)---- 注册和构建原理 0x00 问题的产生 管道是.NET Core中非常关键的一个概念,很多重要的组件都以中间件的形式存在,包括权限管理.会话管理 ...

  2. Python高手之路【二】python基本数据类型

    一:数字 int int(整型): 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,即-2147483648-2147483647 在64位系统上,整数的位数为64位,取值 ...

  3. Canvas绘图之平移translate、旋转rotate、缩放scale

    画布操作介绍 画布绘图的环境通过translate(),scale(),rotate(), setTransform()和transform()来改变,它们会对画布的变换矩阵产生影响. 函数 方法 描 ...

  4. Git命令总结

    本文转载自345大神.... 查看原文 先上个图形化界面GIT工具 SourceTree下载链接 windows版 1.8.3 & Mac版 2.2.4 链接: http://pan.baid ...

  5. 微信小程序初探

    做为码农相信大家最近肯定都会听到微信小程序,虽然现阶段还没有正式开放注册,但大家可以还是可以开发测试. 到微信的WIKI(http://mp.weixin.qq.com/wiki?t=resource ...

  6. ubuntu 下安装scrapy

    1.把Scrapy签名的GPG密钥添加到APT的钥匙环中: sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 6272 ...

  7. TCP/IP基础

    TCP/IP 是用于因特网 (Internet) 的通信协议. 计算机通信协议是对那些计算机必须遵守以便彼此通信的规则的描述. 什么是 TCP/IP? TCP/IP 是供已连接因特网的计算机进行通信的 ...

  8. Linux设备管理(五)_写自己的sysfs接口

    我们在Linux设备管理(一)_kobject, kset,ktype分析一文中介绍了kobject的相关知识,在Linux设备管理(二)_从cdev_add说起和Linux设备管理(三)_总线设备的 ...

  9. 【每日一linux命令1】linux命令路径

    一.路径: 执行命令前必须要考虑的一步是命令的路径,若是路径错误或是没有正确的指定,可能导致错误 的执行或是找不到该命令.要知道设置的路径,可执行以下命令: echo $PATH 显示结果: 这时我们 ...

  10. 学习笔记:7z在delphi的应用

    最近做个发邮件的功能,需要将日志文件通过邮件发送回来用于分析,但是日志文件可能会超级大,测算下来一天可能会有800M的大小.所以压缩是不可避免了,delphi中的默认压缩算法整了半天不太好使,就看了看 ...