epoll 转kqueue的用法介绍和实例 实现跨平台Macos

网上关于kqueue的博客很少 我来补充一个例子echo 的例子

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<sys/event.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>#define MAXLEN 1024
#define SERV_PORT 8000
#define MAX_OPEN_FD 1024// 错误退出的工具函数
int quit(const char *msg){perror(msg);exit(1);
}void setNonBlock(int fd) {int flags = fcntl(fd, F_GETFL, 0);int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}const static int FD_NUM = 2; // 两个文件描述符,分别为标准输入与输出
const static int BUFFER_SIZE = 1024; // 缓冲区大小// 完全以IO复用的方式读入标准输入流数据,输出到标准输出流中
int main(){int  listenfd,connfd,efd,ret;char buf[MAXLEN];struct sockaddr_in cliaddr,servaddr;socklen_t clilen = sizeof(cliaddr);struct kevent tep[2],ep[MAX_OPEN_FD];listenfd = socket(AF_INET,SOCK_STREAM,0);servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,20);struct kevent changes[FD_NUM];struct kevent events[FD_NUM];// 创建一个kqueueint kq;
//    if( (kq = kqueue()) == -1 ) quit("kqueue()");kq = kqueue();//kqueue(); 对应epoll_create// 设置为非阻塞setNonBlock(listenfd);// 注册监听事件int k = 0;// EV_SET代替epoll//tep.events = EPOLLIN;//tep.data.fd = connfd;EV_SET(&changes[k++], listenfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)listenfd);EV_SET(&changes[k++], listenfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)listenfd);kevent(kq, changes, FD_NUM, NULL, 0, NULL);//kevent 可以同时代替epoll_ctl和epoll_wait 生成的实例也就是调用epoll_ctl的时候只需要第2第3 参数 而代替epoll_wait的时候需要第4第5参数int nev, nread, nwrote = 0; // 发生事件的数量, 已读字节数, 已写字节数char buffer[BUFFER_SIZE];int lastActive_;const int kMaxEvents = 2000;struct kevent activeEvs_[kMaxEvents];while(1){//lastActive_ 活跃的事件数量lastActive_ = kevent(kq, NULL, 0, activeEvs_, kMaxEvents, NULL); // 已经就绪的文件描述符数量 epoll_wait
//        if( nev <= 0 ) quit("kevent()");int i;for(i=0; i<lastActive_; i++){struct kevent event = activeEvs_[i];if( event.flags & EV_ERROR ) quit("Event error");int ev_fd = (int)(intptr_t)activeEvs_[i].udata;if (ev_fd == listenfd ){connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);printf("connfd=%d",connfd);setNonBlock(connfd);EV_SET(&changes[0], connfd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)connfd);EV_SET(&changes[1], connfd, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, (void *) (intptr_t)connfd);kevent(kq, changes, 2, NULL, 0, NULL);}// 否则,读取数据else{int bytes = read(ev_fd,buf,MAXLEN);// 客户端关闭连接if (bytes == 0){close(ev_fd);printf("client[%d] closed\n", i);}else{for (int j = 0; j < bytes; ++j){buf[j] = toupper(buf[j]);//把小写字母装换为大写}// 向客户端发送数据write(ev_fd,buf,bytes);}}}}return 0;
}

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 是IO复用的函数,其签名为:

int kevent(int kq, const struct kevent *changelist, // 监视列表int nchanges, // 长度struct kevent *eventlist, // kevent函数用于返回已经就绪的事件列表int nevents, // 长度const struct timespec *timeout); // 超时限制

附上原epoll的实例方便对比

#include<stdio.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<ctype.h>
#define MAXLEN 1024
#define SERV_PORT 8000
#define MAX_OPEN_FD 1024int main(int argc,char *argv[])
{int  listenfd,connfd,efd,ret;char buf[MAXLEN];struct sockaddr_in cliaddr,servaddr;socklen_t clilen = sizeof(cliaddr);struct epoll_event tep,ep[MAX_OPEN_FD];listenfd = socket(AF_INET,SOCK_STREAM,0);servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr));listen(listenfd,20);// 创建一个epoll fdefd = epoll_create(MAX_OPEN_FD);tep.events = EPOLLIN;tep.data.fd = listenfd;// 把监听socket 先添加到efd中ret = epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);// 循环等待for (;;){// 返回已就绪的epoll_event,-1表示阻塞,没有就绪的epoll_event,将一直等待size_t nready = epoll_wait(efd,ep,MAX_OPEN_FD,-1);for (int i = 0; i < nready; ++i){// 如果是新的连接,需要把新的socket添加到efd中if (ep[i].data.fd == listenfd ){connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&clilen);tep.events = EPOLLIN;tep.data.fd = connfd;ret = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);}// 否则,读取数据else{connfd = ep[i].data.fd;int bytes = read(connfd,buf,MAXLEN);// 客户端关闭连接if (bytes == 0){ret =epoll_ctl(efd,EPOLL_CTL_DEL,connfd,NULL);close(connfd);printf("client[%d] closed\n", i);}else{for (int j = 0; j < bytes; ++j){buf[j] = toupper(buf[j]);}// 向客户端发送数据write(connfd,buf,bytes);}}}}return 0;
}

redis源码研究 里面 EV_SET的最后一个参数为什么是NULL 上面实例如果置为NULL会导致数据接收不到

static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct kevent ke;if (mask & AE_READABLE) {EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;}if (mask & AE_WRITABLE) {EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1) return -1;}return 0;
}static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {aeApiState *state = eventLoop->apidata;struct kevent ke;if (mask & AE_READABLE) {EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);kevent(state->kqfd, &ke, 1, NULL, 0, NULL);}if (mask & AE_WRITABLE) {EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);kevent(state->kqfd, &ke, 1, NULL, 0, NULL);}
}

实际上就是个普通的赋值类似个构造函数 也就是我后面用到了udata 所以当然不赋值没效果喽

#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)

根据伯克利大学的研究,kqueue的性能优于epoll,主要是因为epoll不支持在一个系统调用中进行多个兴趣更新,而kqueue可以使用kevent()来实现这一点。在

还有一篇技术论文对二者的区别和性能进行了比较。在

http://www.eecs.berkeley.edu/~sangjin/2012/12/21/epoll-vs-kqueue.html
在这里插入图片描述

实验依据


在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/447129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

区块链中的密码学,使用ABE结合区块链

ABE 密码学&#xff0c;以及与区块链结合的价值 背景 区块链技术具备篡改难度高、使用成本低、分布式的优点&#xff0c;本应成为各行各业的重要助力。但是由于链上参与方担心自己的核心数据外泄&#xff0c;不愿将自己的核心数据上链&#xff0c;这个原因成为阻止区块链落地…

VMware虚拟机安装Ubuntu系统教程

所使用的文件如下&#xff1a; VMware Workstation 17 Pro ubuntu-22.04.3-desktop-amd64.iso 一、ubuntu 命名规则及各版本一览表 1.ubuntu 命名规则&#xff1a; 例如&#xff1a;ubuntu 16.04 LTS 是长期维护版本&#xff1b;ubuntu 17.04 是新特性版本 前两位数字为发…

C++静态类型成员变量的初始化顺序(单例模式)

对编译器来说&#xff0c;静态成员变量的初始化顺序和析构顺序是一个未定义的行为 #include <string> #include <iostream> using namespace std; class Base{ public:static int b;static int a;}; int Base::b 2; int Base::a b 1;int main() {Base base;cou…

区块链中密码学与安全技术

区块链的定义 区块链的定义&#xff0c;应当是&#xff1a;区块链是一种按照时间顺序将数据进行分布式存储的块链式数据结构&#xff0c;它利用共识机制进行数据验证&#xff0c;利用密码学进行数据保护和用户安全访问&#xff0c;利用智能合约来操作数据&#xff0c;从而成为…

面经:服务器相关

阻塞IO 当你去读一个阻塞的文件描述符时&#xff0c;如果在该文件描述符上没有数据可读&#xff0c;那么它会一直阻塞(通俗一点就是一直卡在调用函数那里)&#xff0c;直到有数据可读。当你去写一个阻塞的文件描述符时&#xff0c;如果在该文件描述符上没有空间(通常是缓冲区)…

如何用区块链保障数据安全和承载数据确权

区块链可以确保数据安全&#xff0c;体现在那些方面呢&#xff1f; 主要是两个维度&#xff0c;一是数据的不可篡改性&#xff1b;另外一个就是数据的隐私安全性。区块链技术本身并不解决任何的安全问题&#xff0c;因此需要搭配安全技术一起使用&#xff0c;比如非对称加密、…

面经:单例模式

侯捷单例 和剑指不同 &#xfffc; 饿汉式 饿汉式的特点是一开始就加载了&#xff0c;如果说懒汉式是“时间换空间”&#xff0c;那么饿汉式就是“空间换时间”&#xff0c;因为一开始就创建了实例&#xff0c;所以每次用到的之后直接返回就好了。饿汉式有两种常见的写法&…

属性加密技术及基于属性的ABE算法的访问控制技术介绍

属性加密技术 基于身份的加密体制简介 基于身份的加密体制可以看作一种特殊的公钥加密&#xff0c;它有如下特点:系统中用户的公钥可以由任意的字符串组成。这些字符串可以是用户在现实中的身份信息&#xff0c;如:身份证号码、用户姓名、电话号码、Email地址等&#xff0c;因…

面经:http协议

总结HTTPS传输过程 客户端先从服务器获取到证书&#xff0c;证书中包含公钥 客户端将证书进行校验 客户端生成一个对称密钥&#xff0c;用证书中的公钥进行加密&#xff0c;发送给服务器 服务器得到这个请求后用私钥进行解密&#xff0c;得到该密钥 客户端以后发出后续的请求&…

基于属性加密的ABE算法的应用场景思考展望

ABE算法先前使用在云计算场景中&#xff0c;和区块链存在交叉应用场景&#xff0c;具体问题体现在 数据的异地存储、云服务器提供商的不可信、管理员能否对自身数据拥有足够的控制能力以及如何保证数据的安全有效共享都是亟需解决的问题。 研究背景&#xff1a; 云计算越来越…

面经:设计模式

什么是接口隔离原则&#xff08;Interface Segregation Principle&#xff09; 定义&#xff1a;客户端不应该依赖它不需要的接口&#xff1b;一个类对另一个类的依赖应该建立在最小的接口上。概括的说就是&#xff1a;建立单一接口&#xff0c;不要建立臃肿庞大的接口。&…

区块链、密码和银行之间的衍生关系

银行场景中密码服务 设置密码 用户在注册的时候&#xff0c;如果使用弱密码&#xff0c;系统会检测出来。我的猜测是将弱密码的hash运算和用户输入的密码hash比对&#xff0c;如果一致&#xff0c;禁止用户注册。 1、不要设置简单密码&#xff0c;您设置的密码必须符合中信银…

面经:多线程 线程池

使用线程池 当进程被初始化后&#xff0c;主线程就被创建了。对于绝大多数的应用程序来说&#xff0c;通常仅要求有一个主线程&#xff0c;但也可以在进程内创建多个顺序执行流&#xff0c;这些顺序执行流就是线程&#xff0c;每一个线程都是独立的。 线程是进程的组成部分&am…

AIgorand区块链中VRF随机函数的应用

VRF&#xff08;Verifiable Random Function&#xff09; 可验证随机函数可以看作是一个随机预言机&#xff0c;即可以通过任意的一个输入&#xff0c;获得一个随机数输出&#xff1a;输出的结果&#xff08;Output&#xff09;是一个随机数&#xff0c;其数值会均匀分布在值域…

AIgorand的相关学习参考链接

相关具体的开发者与SDK链接如下&#xff1a; GoSDKJavaScript SDK 网页链接 测试网申请链接Github存储库链接开发者网址AIgorand官网Telegram电报群综合白皮书MediumNaver Blog领英Linkedin区块链浏览器INC公示钱包地址基金会公示钱包地址Telegram电报群官方 Github地址 相关…

操作系统 内核栈

视频哈工大李治军老师&#xff1a;https://www.bilibili.com/video/BV1d4411v7u7?p12 参考文档&#xff1a;https://blog.csdn.net/SakuraA6/article/details/108810916 学长在我大一推荐我看&#xff0c;p12和p13的内容真的有那么难吗&#xff0c;现在已经是我看的第三遍了还…

区块链技术指南 序章理解感悟

序二 误区一&#xff1a; 区块链是一种颠覆性的新技术。区块链不是一个新的技术&#xff0c;而是一个新的技术的组合。其关键的技术&#xff0c;包括P2P动态组网、基于密码学的共享账本、共识机制&#xff08;拜占庭将军问题&#xff0c;分布式场景下的一致性问题&#xff09…

面经:红黑树 B树 B+树 哈希表

1.对于插入&#xff0c;删除&#xff0c;查找 以及 输出有序序列 这几个操作&#xff0c;红黑树也可以完成&#xff0c;时间复杂度 与 用跳表实现是相同的。 但是&#xff0c;对于按照区间查找数据这个操作&#xff08;比如 [20,300]&#xff09;,红黑树的效率没有跳表高&#…

回溯法和dfs的区别

值得注意&#xff0c;回溯法以深度优先搜索的方式搜索解空间&#xff0c;并且在搜索过程中用剪枝函数避免无效搜索。那为何 回溯算法 深度优先搜索 剪枝函数这一说法没有错&#xff1f; 因为树是特殊的图。简单来说&#xff0c;树是广义的图。再简单来说&#xff0c;树是图。…

C++学习笔记 简单部分

C 数据类型 使用变量来存储各种信息&#xff0c;变量保留的是它所存储的值的内存位置。这意味着&#xff0c;当创建一个变量时&#xff0c;就会在内存中保留一些空间。这段内存空间可以用于存储各种数据类型&#xff08;比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型…