epoll编程框架
来源:互联网 发布:烈火封神翅膀进阶数据 编辑:程序博客网 时间:2024/05/01 13:29
epoll简介
epoll
是Linux内核中的一种可扩展IO事件处理机制,最早在 Linux 2.5.44内核中引入,可被用于代替POSIX select 和 poll 系统调用,并且在具有大量应用程序请求时能够获得较好的性能( 此时被监视的文件描述符数目非常大,与旧的 select 和 poll 系统调用完成操作所需 O(n) 不同, epoll能在O(1)时间内完成操作,所以性能相当高),epoll 与 FreeBSD的kqueue类似,都向用户空间提供了自己的文件描述符来进行操作。
int epoll_create(int size);
创建一个epoll的句柄,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的事件注册函数,第一个参数是 epoll_create() 的返回值,第二个参数表示动作,使用如下三个宏来表示:
EPOLL_CTL_ADD //注册新的fd到epfd中;EPOLL_CTL_MOD //修改已经注册的fd的监听事件;EPOLL_CTL_DEL //从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_event 结构如下:
typedef union epoll_data{ void *ptr; int fd; __uint32_t u32; __uint64_t u64;} epoll_data_t;struct epoll_event {__uint32_t 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队列里。
当对方关闭连接(FIN), EPOLLERR,都可以认为是一种EPOLLIN事件,在read的时候分别有0,-1两个返回值。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
参数events用来从内核得到事件的集合,maxevents 告之内核这个events有多大,这个 maxevents 的值不能大于创建 epoll_create() 时的size,参数 timeout 是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。
EPOLL事件有两种模型 Level Triggered (LT) 和 Edge Triggered (ET):
LT(level triggered,水平触发模式)是缺省的工作方式,并且同时支持 block 和 non-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。
ET(edge-triggered,边缘触发模式)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,等到下次有新的数据进来的时候才会再次出发就绪事件。
epoll编程例子:
把所有其他相关的代码都略去,只留下epoll编程相关的框架代码。其实这个都差不多
int main(int argc, char* argv[]){ //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 struct epoll_event ev,events[20];//生成用于处理accept的epoll专用的文件描述符 epfd=epoll_create(256); //设置与要处理的事件相关的文件描述符 ev.data.fd=listenfd; //设置要处理的事件类型 ev.events=EPOLLIN|EPOLLET; //注册epoll事件 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); for ( ; ; ) { //等待epoll事件的发生 nfds=epoll_wait(epfd,events,20,500); //处理所发生的所有事件 for(i=0;i<nfds;++i) { if(events[i].data.fd==listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。 { connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //设置用于读操作的文件描述符 ev.data.fd=connfd; //设置用于注测的读操作事件 ev.events=EPOLLIN|EPOLLET; //注册ev epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); } else if(events[i].events&EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。 { cout << "EPOLLIN" << endl; if ( (sockfd = events[i].data.fd) < 0) continue; if ( (n = read(sockfd, line, MAXLINE)) < 0) { if (errno == ECONNRESET) { close(sockfd); events[i].data.fd = -1; } else std::cout<<"readline error"<<std::endl; } else if (n == 0) { close(sockfd); events[i].data.fd = -1; } ev.data.fd=sockfd; //设置用于注测的写操作事件 ev.events=EPOLLOUT|EPOLLET; //修改sockfd上要处理的事件为EPOLLOUT } else if(events[i].events&EPOLLOUT) // 如果有数据发送 { sockfd = events[i].data.fd; write(sockfd, line, n); //设置用于读操作的文件描述符 ev.data.fd=sockfd; //设置用于注测的读操作事件 ev.events=EPOLLIN|EPOLLET; //修改sockfd上要处理的事件为EPOLIN epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } } return 0;}
- epoll编程框架
- epoll编程
- epoll编程实例
- linux服务器编程--EPOLL
- linux 网络编程 epoll
- 网络编程: epoll
- epoll服务器编程-demo
- 网络编程 epoll
- Linux Epoll编程
- linux下epoll编程
- socket编程之epoll
- Linux--编程--epoll
- Linux_C 编程:epoll详解
- epoll网络编程实例
- 网络编程 select epoll
- 【网络编程】epoll简介
- epoll网络编程实例
- linux编程--多路复用---epoll
- 浅析 - Android http GET/POST 请求
- JAVA正则表达式:Pattern类与Matcher类详解(转)
- Spring总结之一 —— AOP
- AngularJS动态生成div的ID
- Python设计模式(九)【外观模式】
- epoll编程框架
- 一句shell命令搞定代码行数统计
- 深搜和广搜s
- uCOS-II移植到STM32上的详细步骤
- Android开发艺术Activity生命周期
- Vijos P1008 篝火晚会(组合数学,置换群)
- Leetcode 213. House Robber II (Medium) (cpp)
- java8之Stream语法详解
- poj2533