关于epoll的原理,以及和poll、select、iocp之间的比较,网上的资料很多,这些都属于i/o复用的实现方法,即可以同时监听发生在多个i/o端口(socket套接字描述符或文件描述符)的事件,并将事件从内核通知到用户区,实现对特定事件的响应处理,而epoll可认为是poll的改进版,在多个方面大幅度提高了性能(当然也是在监听描述符多、活跃描述符少的条件下)。
epoll的主要特点有以下几点:
1.支持一个进程打开最大数目的socket描述符,通常数目只受限于系统内存;2.io效率不随fd数目的增加而下降,它只对“活跃”的socket进行操作;3.使用内存映射加速内核与用户空间的消息传递。这里只是简单介绍了epoll的几个重要特征,总之,epoll的高性能使其在服务器网络连接层开发中应用的很广泛,包括很多开源的服务器框架底层也采用了epoll。下面我们主要来设计实现一个epoll操作封装类。
首先说明一下,epoll主要三个操作函数:
int epoll_create(int size);创建一个epoll的句柄。自从linux2.6.8之后,size参数是被忽略的。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
第一个参数是epoll_create()的返回值。
第二个参数表示动作,用三个宏来表示:
epoll_ctl_add:注册新的fd到epfd中;
epoll_ctl_mod:修改已经注册的fd的监听事件;
epoll_ctl_del:从epfd中删除一个fd;
第三个参数是需要监听的fd。
第四个参数是告诉内核需要监听什么事件,struct epoll_event结构如下:
typedef union epoll_data{pointer ptr;int fd;uint u32;uint64 u64;} epoll_data_t;struct epoll_event{uint events; /* epoll events */epoll_data_t data; /* user data variable */};events可以是以下几个宏的集合:
epollin :表示对应的文件描述符可以读(包括对端socket正常关闭);epollout:表示对应的文件描述符可以写;epollpri:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);epollerr:表示对应的文件描述符发生错误;epollhup:表示对应的文件描述符被挂断;epollet:将epoll设为边缘触发(edge triggered)模式,这是相对于水平触发(level triggered)来说的。epolloneshot:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到epoll队列里。epoll_data_t是一个联合结构,64位大小,可以存fd。这里具体实现中我们存一个cepollobject对象的指针,以确保epoll_wait从网络中接收到的消息确实是我们通过一个cepollobject对象监听到的。
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应i/o上已准备好的文件描述符数目,如返回0表示已超时。
epoll封装类实现
设计思想:通过一个模板类实现向epoll注册、修改和删除事件等操作,需要使用epoll类都必须走这个模块类,类似一种委托的功能回调模板类实例化对象的epoll监听事件响应处理操作,主要实现类:cepollobjectinf、cepoll和cepollobject模板类。
cepollobjectinf类的实现
主要功能:表达epoll_data_t的存储内容,以及提供对epoll_wait监听到的事件提供响应处理接口
实现代码:
#include #include #include #include #define invaild_soket (~0)class cepoll;class cepollobjectinf{ friend class cepoll; protected: //这两个变量为cepoll的waitandevent中做对象合法检验, //如果是64位系统,则不要socket变量 cepoll *m_pstepoll; socket m_isocket; public: cepollobjectinf() :m_pstepoll(null), m_isocket(invaild_soket ) {} virtual cepollobjectinf(){} protected: virtual void onepollevent(int ievent) = 0;}cepoll类的实现
主要功能:封装epoll的各项操作
代码实现:
#define uint64_make(high, low) ((uint64)(((unsigned int)((low) & 0xffffffff)) | ((uint64)((unsigned int)((high) & 0xffffffff))) < > 32))class cepoll{ public: cepoll() :m_kdpfd(0), m_size(0), m_iwaitsize(0), m_astevents(0) {} virtual ~cepoll() { exit(); } public: //初始化 int init(int iepollsize, int iwaitsize) { m_size = iepollsize; m_iwaitsize = iwaitsize; m_astevents = new epoll_event[m_iwaitsize]; if(!m_astevents) return -1; m_kdpfd = epoll_create(m_size); if(m_kdpfd < 0) return -2; return 0; } /** *等待时间发生或超时 *itimeout 等待的超时时限单位毫秒 *return 0 表示收到并处理的事件个数 **/ int wait(int itimeout) { return epoll_wait(m_kdpfd,m_astevents,m_iwaitsize,itimeout); } /** *等待事件发生或超时,并调用方法 *itimeout 等待超时时限,单位毫秒 *return 0 表示收到并处理的事件个数 */ int waitandevent(int itimeout) { int ieventcount = wait(itimeout); if(ieventcount < 0) { return ieventcount; } else if(ieventcount == 0) // 超时 { return 0; } //一次最多处理1000个事件 for(int i = 0;i < ieventcount && i m_pstepoll != this) { //不处理本次事件,继续处理下一个事件 continue; } pstobjectpos- >onepollevent(uievent); } return ieventcount; } uint64 getdata(int i) const { assert(i < m_iwaitsize) return m_astevents[i].data.u64; } uint getevent(int i) const { assert(i 0) { close(m_kdpfd); mkdpfd = 0; } } protected: int m_kdpfd; int m_size; int m_iwaitsize; struct epoll_event *m_astevents; struct epoll_event m_stevent;};cepollobject模版类的实现
主要功能:托管cepoll类的具体操作,注册事件到epoll,必须实例化cepollobject模版类,并覆盖实现具体的事件处理函数,以回调不同对象对事件的处理函数。
代码实现:
template class cepollobject: public cepollobjectinf{ friend class cepoll; public: typedef void (owner::*pf_epoll_event)(cepollobject *pstobject,socket isocket, int ievnet); protected: owner *m_pstowner; pf_epoll_event m_pfevent; unsigned int m_iregevent; public: cepollobject() :m_pstowner(null), m_pfevent(null), m_iregevent(0) {} virtual ~cepollobject() {unregister();} /** *注册到epoll中 **/ int register(owner &stowner, pf_epoll_event pfevent,cepoll &stepoll,socket isocket, unsigned int iregevent){ assert(isocket != invalid_socket && iregevent > 0 && pfevent != null); int iret = unregister(); if(iret) return iret; m_pstowner = &stowner; m_pstepoll = &stepoll; m_pfevent = pfevent; m_iregevent = iregevent; m_isocket = isocket; uint64 udata = createdata(m_isocket); iret = m_pstepoll- >add(m_isocket,udata,m_iregevent); return iret; } /** *更改关注的事件 **/ int modregevent(int iregevent){ m_iregevent = iregevent; if(m_pstepoll) { uint64 udata = createdata(m_isocket); return m_pstepoll- >mod(m_isocket,udata,m_iregevent); } return 0; } protected: virtual void onepollevent(int ievent){ assert(m_pstowner != null && m_pfevent != null); (m_pstowner- >*m_pfevent)(this,m_isocket,ievent); } int unregister(){ int iret = 0; if(m_pstepoll) { iret = m_pstepoll- >del(m_isocket); m_pstepoll = null; } m_pstowner = null; m_pfevent = null; m_iregevent = 0; m_isocket = invalid_socket; return iret; } uint64 createdata(socket isocket){ #ifdef bit64 return (uint64)this; #else return uint64_make(isocket, (unsigned int)this); #endif }};
意法半导体惯性传感器的工艺设计及封装方法
bluehost美国站群服务器做网站优化效果怎么样
经验总结| 秸秆焚烧监控预警解决方案的使用好处多多
欧姆龙微动开关要如何满足智能锁的市场需求?
恩智浦推出用于实现5G基础设施的新型射频前端解决方案
Epoll封装类实现
textCNN论文与原理——短文本分类
明基宣布了为设计师量身定做的新型明基PD3420Q超宽显示器
PID控制规律及特点
小米6和华为P10都要涨价,你们准备好了吗?
华为:今年不会在美国出售Mate 20和Mate 20 Pro手机
Boost Converter Generates Thre
是德科技公司于CIG合作完成IODT
兼具稳定性与价格优势的高性价比DTU,为煤改电提供稳定数据传输
不漏音的骨传导耳机,盘点几个骨传导技术最好的品牌
亿纬动力与Aksa拟在土耳其组建合资公司
数字芯片和模拟芯片有哪些特性
西门子S7系列PLC定时器工作状态
为什么工程师创业总是失败
索尼α9发布:售31999 元,黑科技终问世,索尼大法好!