kqueue用法简介
1.什么是kqueue和IO复用
kueue是在UNIX上比较高效的IO复用技术。
所谓的IO复用,就是同时等待多个文件描述符就绪,以系统调用的形式提供。如果所有文件描述符都没有就绪的话,该系统调用阻塞,否则调用返回,允许用户进行后续的操作。
常见的IO复用技术有select, poll, epoll以及kqueue等等。其中epoll为Linux独占,而kqueue则在许多UNIX系统上存在,包括OS X(好吧,现在叫macOS了。。)
2. 使用概览
kueue在设计上是非常简洁的,在易用性上可能比select和epoll更好一些。
使用kqueue的大致代码如下:(后面会给出一个完整的示例)
const static int FD_NUM = 2 // 要监视多少个文件描述符
int kq = kqueue(); // kqueue对象
// kqueue的事件结构体,不需要直接操作
struct kevent changes[FD_NUM]; // 要监视的事件列表
struct kevent events[FD_NUM]; // kevent返回的事件列表(参考后面的kevent函数)
int stdin_fd = STDIN_FILENO;
int stdout_fd = STDOUT_FILENO;
// 在changes列表中注册标准输入流的读事件 以及 标准输出流的写事件
// 最后一个参数可以是任意的附加数据(void * 类型),在这里给事件附上了当前的文件描述符,后面会用到
EV_SET(&changes[0], stdin_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, &stdin_fd);
EV_SET(&changes[1], stdout_fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, &stdin_fd);
// 进行kevent函数调用,如果changes列表里有任何就绪的fd,则把该事件对应的结构体放进events列表里面
// 返回值是这次调用得到了几个就绪的事件 (nev = number of events)
int nev = kevent(kq, changes, FD_NUM, events, FD_NUM, NULL); // 已经就绪的文件描述符数量
for(int i=0; i<nev; i++){
struct kevent event = events[i]; // 一个个取出已经就绪的事件
int ready_fd = *((int *)event.udata); // 从附加数据里面取回文件描述符的值
if( ready_fd == stdin_fd ){
// 读取ready_fd
}else if( ready_fd == stdin_fd ){
// 写入ready_fd
}
}
3. 相关结构体与函数解析
可以看出来,kqueue体系只有三样东西:struct kevent结构体,EV_SET宏以及kevent函数。
struct kevent 结构体内容如下:
struct kevent {
uintptr_t ident; /* identifier for this event,比如该事件关联的文件描述符 */
int16_t filter; /* filter for event,可以指定监听类型,如EVFILT_READ,EVFILT_WRITE,EVFILT_TIMER等 */
uint16_t flags; /* general flags ,可以指定事件操作类型,比如EV_ADD,EV_ENABLE, EV_DELETE等 */
uint32_t fflags; /* filter-specific flags */
intptr_t data; /* filter-specific data */
void *udata; /* opaque user data identifier,可以携带的任意数据 */
};
EV_SET 是用于初始化kevent结构的便利宏,其签名为:
EV_SET(&kev, ident, filter, flags, fflags, data, udata);
可以发现和kevent结构体完全对应,除了第一个,它就是你要初始化的那个kevent结构。
kevent 是真正进行IO复用的函数,其签名为:
int kevent(int kq,
const struct kevent *changelist, // 监视列表
int nchanges, // 长度
struct kevent *eventlist, // kevent函数用于返回已经就绪的事件列表
int nevents, // 长度
const struct timespec *timeout); // 超时限制
4. 完整示例
下面给出一个完整的示例,这个程序将从标准输入中读取数据,写到标准输出中。其中输入输出全部使用kqueue来进行IO复用。可以使用重定向把文件写入标准输入来进行测试。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/event.h>
#include <errno.h>
#include <string.h>
// 为文件描述符打开对应状态位的工具函数
void turn_on_flags(int fd, int flags){
int current_flags;
// 获取给定文件描述符现有的flag
// 其中fcntl的第二个参数F_GETFL表示要获取fd的状态
if( (current_flags = fcntl(fd, F_GETFL)) < 0 ) exit(1);
// 施加新的状态位
current_flags |= flags;
if( fcntl(fd, F_SETFL, current_flags) < 0 ) exit(1);
}
// 错误退出的工具函数
int quit(const char *msg){
perror(msg);
exit(1);
}
const static int FD_NUM = 2; // 两个文件描述符,分别为标准输入与输出
const static int BUFFER_SIZE = 1024; // 缓冲区大小
// 完全以IO复用的方式读入标准输入流数据,输出到标准输出流中
int main(){
struct kevent changes[FD_NUM];
struct kevent events[FD_NUM];
// 创建一个kqueue
int kq;
if( (kq = kqueue()) == -1 ) quit("kqueue()");
// 准备从标准输入流中读数据
int stdin_fd = STDIN_FILENO;
int stdout_fd = STDOUT_FILENO;
// 设置为非阻塞
turn_on_flags(stdin_fd, O_NONBLOCK);
turn_on_flags(stdout_fd, O_NONBLOCK);
// 注册监听事件
int k = 0;
EV_SET(&changes[k++], stdin_fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, &stdin_fd);
EV_SET(&changes[k++], stdout_fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, &stdout_fd);
int nev, nread, nwrote = 0; // 发生事件的数量, 已读字节数, 已写字节数
char buffer[BUFFER_SIZE];
while(1){
nev = kevent(kq, changes, FD_NUM, events, FD_NUM, NULL); // 已经就绪的文件描述符数量
if( nev <= 0 ) quit("kevent()");
int i;
for(i=0; i<nev; i++){
struct kevent event = events[i];
if( event.flags & EV_ERROR ) quit("Event error");
int ev_fd = *((int *)event.udata);
// 输入流就绪 且 缓冲区还有空间能继续读
if( ev_fd == stdin_fd && nread < BUFFER_SIZE ){
int new_nread;
if( (new_nread = read(ev_fd, buffer + nread, sizeof(buffer) - nread)) <= 0 )
quit("read()"); // 由于可读事件已经发生,因此如果读出0个字节也是不正常的
nread += new_nread; // 递增已读数据字节数
}
// 输出流就绪 且 缓冲区有内容可以写出
if( ev_fd == stdout_fd && nread > 0 ){
if( (nwrote = write(stdout_fd, buffer, nread)) <=0 )
quit("write()");
memmove(buffer, buffer+nwrote, nwrote); // 为了使实现的代码更简洁,这里把还没有写出去的数据往前移动
nread -= nwrote; // 减去已经写出去的字节数
}
}
}
return 0;
}
程序中对stdin和stdout设置非阻塞的原因是我们希望有多少就绪的数据就读多少,或者能写入多少进缓冲区就写入多少。否则在阻塞模式下,如果read没有填满buffer(文件没读完时),或者还有buffer数据没写入时,系统调用(read和write)会阻塞,这会对性能造成很大影响。因此这里设置为非阻塞模式。
kqueue用法简介的更多相关文章
- IOS NSInvocation用法简介
IOS NSInvocation用法简介 2012-10-25 19:59 来源:博客园 作者:csj007523 字号:T|T [摘要]在 iOS中可以直接调用某个对象的消息方式有两种,其中一种就是 ...
- JodaTime用法简介
JodaTime用法简介 Java的Date和Calendar用起来简直就是灾难,跟C#的DateTime差距太明显了,幸好有JodaTime 本文简单罗列JodaTime的用法 package co ...
- Apache自带压力测试工具ab用法简介
ab命令原理 ab命令会创建很多的并发访问线程,模拟多个访问者同时对某一URL进行访问.它的测试目标是基于URL的,因此,既可以用来测试Apache的负载压力,也可以测试nginx.lighthttp ...
- Postman用法简介
转自:http://blog.csdn.net/flowerspring/article/details/52774399 Postman用法简介 转载 2016年10月10日 09:04:10 10 ...
- MSSQL Sql加密函数 hashbytes 用法简介
转自:http://www.maomao365.com/?p=4732 一.mssql sql hashbytes 函数简介 hashbytes函数功能为:返回一个字符,通过 MD2.MD4.MD5. ...
- java assert的用法简介【转】
assert的基本用法 assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制,如C,C++和Eiffel等,但是支持的形式不尽相同,有的是通过语言本身.有的是通过库 ...
- glVertexAttribPointer 用法简介
在内存中采用交叉模式存储,向gpu传入顶点数据的方法 GPU: #version 100 attribute highp vec2 aPosition; attribute highp vec2 a ...
- C#中IPAddress类/Dns类/IPHostEntry类/IPEndPoint用法简介
C#中IPAddress类/Dns类/IPHostEntry类/IPEndPoint用法简介 IP是一种普遍应用于因特网.允许不同主机能够相互找到对方的寻址协议.IP地址由4个十进制的数字号码所组成, ...
- Postman用法简介----https://blog.csdn.net/flowerspring/article/details/52774399
https://blog.csdn.net/flowerspring/article/details/52774399 Postman用法简介
随机推荐
- ASP.NET 导出gridview中的数据到Excel表中,并对指定单元格换行操作
1. 使用NPOI读取及生成excel表. (1)导出Click事件: 获取DataTable; 给文件加文件名: string xlsxName = "xxx_" + DateT ...
- css布局模型
---恢复内容开始--- 在清楚了CSS盒模型的基本概念,盒模型类型,我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是CSS最基本,最核心的概念.但布局模型是建立在盒型基础之上,又不同 ...
- iOS图片如何按比例显示
文/罚难(简书作者)原文链接:http://www.jianshu.com/p/ec7d3f210983著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 只需加这么一段代码,如下: im ...
- distinct order by 排序问题
使用类似“SELECT DISTINCT `col` FROM `tb_name` ORDER BY `time` DESC”这样的sql语句时,会遇到排序问题. 以上面的sql语句分析:order ...
- crtmpserver系列(一):流媒体概述
概述 所谓流媒体按照字面意思理解就是像流一样的媒体,看起来像是废话.流媒体现在司空见惯,所以一般人大概不会有疑问.事实上在流媒体还没有出现的时候,基本上通过网络播放电影就不太现实.通过网络播放电影的时 ...
- MVC与DWZ整合中部分问题的解决
1.错误提示距离太远 2.正确与错误时返回JSON(即:如何不出现打开新页显示JSON字串) 3.打开新页后“数据加载中,请稍候”关不掉 4.如何正常的分页,同时如果有查询结果时分页也要有效 5.关闭 ...
- 解决.NET Core中MailKit无法使用阿里云邮件推送服务的问题
在博问中(.net core怎么实现邮件发送)知道了MailKit无法使用阿里云邮件推送服务发送邮件的问题,自已实测也遇到同样的问题,而用自己搭建的邮件服务器没这个问题. 于是,向阿里云提交了工单.. ...
- Matrix Admin 后台模板笔记
一个后台模板用久了就想换一个.上次找到了Matrix Admin.和ACE一样都是Bootstrap风格,比较容易上手.Matrix要更健壮些.感觉拿去做用户界面也是可以的. 整体风格: 1.表单验证 ...
- 客户端GUI程序开发漫谈
这篇文章包含了这个领域的很多开源项目的介绍,还有我多年来的心血和汗水 去年夏天的时候,我用QT做了一个小工具 后来还用QT做了流程设计器 我把程序分享给飞扬青云之后,他甚至搞出来一套QT的皮肤来 说 ...
- 运用DebugDiag诊断ASP.Net异常
Debug Diagnostic Tool (DebugDiag)是用来帮助诊断IIS/COM+等应用假死.性能差.内存泄露及碎片和崩溃等问题的工具. 本文介绍如何运用DebugDiag诊断特定的AS ...