epoll使用详解

合集下载

epoll的执行流程

epoll的执行流程

epoll的执行流程epoll 主要有三个系统调用函数:epoll_create、epoll_ctl 和 epoll_wait。

其中 epoll_create 用于创建一个 epoll 实例,epoll_ctl 用于向 epoll 实例中添加、修改或删除要监视的文件描述符,而 epoll_wait 则是用于等待事件的发生。

下面我们将详细介绍 epoll 的执行流程。

1. 创建 epoll 实例首先,我们需要创建一个 epoll 实例。

这可以通过调用 epoll_create 函数来完成。

该函数将返回一个用于标识 epoll 实例的文件描述符。

在内核中,epoll 实例是由一个红黑树和一个链表组成的数据结构,用于存储监视的文件描述符及其状态。

2. 向 epoll 实例中添加文件描述符一旦有了 epoll 实例,我们就可以通过 epoll_ctl 函数向其中添加要监视的文件描述符。

epoll_ctl 函数的原型为:```cint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);```其中,epfd 是 epoll 实例的文件描述符,op 是操作类型(EPOLL_CTL_ADD、EPOLL_CTL_MOD 或 EPOLL_CTL_DEL),fd 是要监视的文件描述符,event 是一个epoll_event 结构体,用于描述该文件描述符的监听事件。

epoll_event 结构体的定义如下:```ctypedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64;} epoll_data_t;struct epoll_event {uint32_t events; // 监视的事件类型epoll_data_t data; // 文件描述符的附加数据};```在 epoll_ctl 函数中,我们可以指定要监听的事件类型,例如 EPOLLIN(可读事件)、EPOLLOUT(可写事件)等。

epoll使用细解

epoll使用细解

epoll相关的系统调用有:epoll_create, epoll_ctl和epoll_wait。

Linux-2.6.19又引入了可以屏蔽指定信号的epoll_wait: epoll_pwait。

至此epoll家族已全。

其中epoll_create用来创建一个epoll文件描述符,epoll_ctl用来添加/修改/删除需要侦听的文件描述符及其事件,epoll_wait/epoll_pwait接收发生在被侦听的描述符上的,用户感兴趣的IO事件。

epoll文件描述符用完后,直接用close关闭即可,非常方便。

事实上,任何被侦听的文件符只要其被关闭,那么它也会自动从被侦听的文件描述符集合中删除,很是智能。

每次添加/修改/删除被侦听文件描述符都需要调用epoll_ctl,所以要尽量少地调用epoll_ctl,防止其所引来的开销抵消其带来的好处。

有的时候,应用中可能存在大量的短连接(比如说Web服务器),epoll_ctl将被频繁地调用,可能成为这个系统的瓶颈。

A:IO效率。

在大家苦苦的为在线人数的增长而导致的系统资源吃紧上的问题正在发愁的时候,Linux 2.6内核中提供的System Epoll为我们提供了一套完美的解决方案。

传统的select以及poll的效率会因为在线人数的线形递增而导致呈二次乃至三次方的下降,这些直接导致了网络服务器可以支持的人数有了个比较明显的限制。

自从Linux提供了/dev/epoll的设备以及后来2.6内核中对/dev/epoll设备的访问的封装(System Epoll)之后,这种现象得到了大大的缓解,如果说几个月前,大家还对epoll不熟悉,那么现在来说的话,epoll的应用已经得到了大范围的普及。

那么究竟如何来使用epoll呢?其实非常简单。

通过在包含一个头文件#include <sys/epoll.h>以及几个简单的API 将可以大大的提高你的网络服务器的支持人数。

epoll的LT和ET使用EPOLLONESHOT

epoll的LT和ET使用EPOLLONESHOT

epoll的LT和ET使⽤EPOLLONESHOTepoll有两种触发的⽅式即LT(⽔平触发)和ET(边缘触发)两种,在前者,只要存在着事件就会不断的触发,直到处理完成,⽽后者只触发⼀次相同事件或者说只在从⾮触发到触发两个状态转换的时候⼉才触发。

这会出现下⾯⼀种情况,如果是多线程在处理,⼀个SOCKET事件到来,数据开始解析,这时候这个SOCKET⼜来了同样⼀个这样的事件,⽽你的数据解析尚未完成,那么程序会⾃动调度另外⼀个线程或者进程来处理新的事件,这造成⼀个很严重的问题,不同的线程或者进程在处理同⼀个SOCKET的事件,这会使程序的健壮性⼤降低⽽编程的复杂度⼤⼤增加!!即使在ET模式下也有可能出现这种情况!!解决这种现象有两种⽅法:第⼀种⽅法是在单独的线程或进程⾥解析数据,也就是说,接收数据的线程接收到数据后⽴刻将数据转移⾄另外的线程。

第⼆种⽅法就是本⽂要提到的EPOLLONESHOT这种⽅法,可以在epoll上注册这个事件,注册这个事件后,如果在处理写成当前的SOCKET后不再重新注册相关事件,那么这个事件就不再响应了或者说触发了。

要想重新注册事件则需要调⽤epoll_ctl重置⽂件描述符上的事件,这样前⾯的socket就不会出现竞态这样就可以通过⼿动的⽅式来保证同⼀SOCKET只能被⼀个线程处理,不会跨越多个线程。

看下⾯的代码:void Eepoll::ResetOneShot(int epollfd,SOCKET fd,bool bOne){epoll_eventevent;event.data.fd= fd;event.events= EPOLLIN | EPOLLET ;if(bOne){event.events |=EPOLLONESHOT;}if(-1 == epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event)){perror("resetoneshotepoll_ctl error!");}}这⾥有⼀个问题,在操作ET模式下的EPOLL时,对EPOLLONESHOT没有什么太⼤的注意点,但是在LT时,就有⼀些注意的了。

Epoll模型详解

Epoll模型详解

在linux的网络编程中,很长的时间都在使用select来做事件触发。

在linux新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。

因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

并且,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll的接口非常简单,一共就三个函数:1. int epoll_create(int size);创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。

这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。

需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2.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结构如下: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 T riggered)模式,这是相对于水平触发(Level T riggered)来说的。

深入理解epoll讲解

深入理解epoll讲解

深入理解epoll1、基本知识epoll是在2.6内核中提出的,是之前的select和poll的增强版本。

相对于select和poll 来说,epoll更加灵活,没有描述符限制。

epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

2、epoll接口epoll操作过程需要三个接口,分别如下:[cpp] view plain copy 在CODE上查看代码片派生到我的代码片#include <sys/epoll.h>int epoll_create(int size);int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);(1)int epoll_create(int size);创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。

这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。

需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在Linux下如果查看/proc/进程id/fd/,是能够看到这个fd 的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

(2)int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。

epoll基本处理流程

epoll基本处理流程

epoll基本处理流程下载温馨提示:该文档是我店铺精心编制而成,希望大家下载以后,能够帮助大家解决实际的问题。

文档下载后可定制随意修改,请根据实际需要进行相应的调整和使用,谢谢!并且,本店铺为大家提供各种各样类型的实用资料,如教育随笔、日记赏析、句子摘抄、古诗大全、经典美文、话题作文、工作总结、词语解析、文案摘录、其他资料等等,如想了解不同资料格式和写法,敬请关注!Download tips: This document is carefully compiled by theeditor. I hope that after you download them,they can help yousolve practical problems. The document can be customized andmodified after downloading,please adjust and use it according toactual needs, thank you!In addition, our shop provides you with various types ofpractical materials,such as educational essays, diaryappreciation,sentence excerpts,ancient poems,classic articles,topic composition,work summary,word parsing,copy excerpts,other materials and so on,want to know different data formats andwriting methods,please pay attention!1. 创建 epoll 实例使用 `epoll_create` 函数创建一个 epoll 实例。

epoll编程实例

epoll编程实例

epoll编程实例epoll是Linux内核提供的一种高效的I/O多路复用技术,可以在单线程中监控多个文件描述符,不断返回可以操作的文件描述符集合,从而避免了针对大量连接的轮询。

在网络编程中,可以使用epoll来实现高并发、高性能的网络服务器。

本文将介绍一个简单的epoll编程实例,实现一个基本的echo服务器。

该服务器使用TCP协议进行通信,可以同时处理多个客户端连接,将客户端发送的消息原样返回。

首先,我们需要创建一个监听socket,用于接受客户端的连接请求。

代码如下:```int listen_fd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(8888);bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));listen(listen_fd, 1024);```接下来,我们需要使用epoll对listen_fd进行监控。

首先,我们需要创建一个epoll实例:```int epoll_fd = epoll_create(1024);```返回一个文件描述符epoll_fd,用于操作epoll实例。

参数1024表示监听的事件数目。

然后,我们需要将listen_fd添加到epoll实例中:```struct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = listen_fd;epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev);```这里设置监听事件为EPOLLIN,表示可读事件。

将listen_fd添加到epoll实例中后,epoll_wait函数将在该文件描述符上发生事件时返回。

epoll使用实例说明

epoll使用实例说明

epoll使⽤实例说明之前⼀直在讲如何epoll如何好⽤,但是并没有实例来演⽰epoll的使⽤,下⾯我们就看⼀个服务器端使⽤epoll监听⼤量并发链接的例⼦。

⾸先看⼀下epoll的⼏个函数的介绍。

1、epoll_create函数/*** @brief 该函数⽣成⼀个epoll专⽤的⽂件描述符。

它其实是在内核申请⼀空间,⽤来存放你想关注的socket fd上是否发⽣以及发⽣了什么事件。

** @param size size就是你在这个epoll fd上能关注的最⼤socket fd数** @return ⽣成的⽂件描述符*/int epoll_create(int size);2、epoll_ctl函数/*** @brief 该函数⽤于控制某个epoll⽂件描述符上的事件,可以注册事件,修改事件,删除事件。

** @param epfd 由 epoll_create ⽣成的epoll专⽤的⽂件描述符* @param op 要进⾏的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修改、EPOLL_CTL_DEL 删除 * @param fd 关联的⽂件描述符* @param event 指向epoll_event的指针** @return 0 succ* -1 fail*/int epoll_ctl(int epfd, int op, int fd, struct epoll_event *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 */};常⽤的事件类型:EPOLLIN :表⽰对应的⽂件描述符可以读;EPOLLOUT:表⽰对应的⽂件描述符可以写;EPOLLPRI:表⽰对应的⽂件描述符有紧急的数据可读EPOLLERR:表⽰对应的⽂件描述符发⽣错误;EPOLLHUP:表⽰对应的⽂件描述符被挂断;EPOLLET:表⽰对应的⽂件描述符有事件发⽣;例:struct epoll_event ev;//设置与要处理的事件相关的⽂件描述符ev.data.fd=listenfd;//设置要处理的事件类型ev.events=EPOLLIN|EPOLLET;//注册epoll事件epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);3、epoll_wait函数/*** @brief 该函数⽤于轮询I/O事件的发⽣** @param epfd 由epoll_create ⽣成的epoll专⽤的⽂件描述符* @param events ⽤于回传代处理事件的数组* @param maxevents 每次能处理的事件数* @param timeout 等待I/O事件发⽣的超时值;-1相当于阻塞,0相当于⾮阻塞。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

epoll使用详解(精髓)分类: Linux程序及使用 2009-04-11 16:34 9001人阅读评论(6) 收藏举报epoll - I/O event notification facility在linux的网络编程中,很长的时间都在使用select来做事件触发。

在linux新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。

因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

并且,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE 1024表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll的接口非常简单,一共就三个函数:1. int epoll_create(int size);创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。

这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。

需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2. 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 {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队列里3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 等待事件的产生,类似于select()调用。

参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。

该函数返回需要处理的事件数目,如返回0表示已超时。

4、关于ET、LT两种工作模式:可以得出这样的结论:ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET 模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT模式是只要有数据没有处理就会一直通知下去的.那么究竟如何来使用epoll呢?其实非常简单。

通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。

这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。

在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。

基本的语法为:nfds = epoll_wait(kdpfd, events, maxevents, -1);其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。

max_events是当前需要监听的所有socket句柄数。

最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则范围。

一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。

epoll_wait范围之后应该是一个循环,遍利所有的事件。

几乎所有的epoll程序都使用下面的框架:for( ; ; ){nfds = epoll_wait(epfd,events,20,500);for(i=0;i<nfds;++i){if(events[i].data.fd==listenfd) //有新的连接{connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept这个连接ev.data.fd=connfd;ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //将新的fd添加到epoll的监听队列中}else if( events[i].events&EPOLLIN ) //接收到数据,读socket{n = read(sockfd, line, MAXLINE)) < 0 //读ev.data.ptr = md; //md为自定义类型,添加数据ev.events=EPOLLOUT|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓}else if(events[i].events&EPOLLOUT) //有数据待发送,写socket{struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取数据sockfd = md->fd;send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //发送数据 ev.data.fd=sockfd;ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据}else{//其他的处理}}}下面给出一个完整的服务器端例子:#include <iostream>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <errno.h>using namespace std;#define MAXLINE 5#define OPEN_MAX 100#define LISTENQ 20#define SERV_PORT 5000#define INFTIM 1000void setnonblocking(int sock){int opts;opts=fcntl(sock,F_GETFL);if(opts<0){perror("fcntl(sock,GETFL)");exit(1);}opts = opts|O_NONBLOCK;if(fcntl(sock,F_SETFL,opts)<0){perror("fcntl(sock,SETFL,opts)");exit(1);}}int main(int argc, char* argv[]){int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber; ssize_t n;char line[MAXLINE];socklen_t clilen;if ( 2 == argc ){if( (portnumber = atoi(argv[1])) < 0 ){fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]); return 1;}}else{fprintf(stderr,"Usage:%s portnumber/a/n",argv[0]);return 1;}//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件struct epoll_event ev,events[20];//生成用于处理accept的epoll专用的文件描述符epfd=epoll_create(256);struct sockaddr_in clientaddr;struct sockaddr_in serveraddr;listenfd = socket(AF_INET, SOCK_STREAM, 0);//把socket设置为非阻塞方式//setnonblocking(listenfd);//设置与要处理的事件相关的文件描述符ev.data.fd=listenfd;//设置要处理的事件类型ev.events=EPOLLIN|EPOLLET;//ev.events=EPOLLIN;//注册epoll事件epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);bzero(&serveraddr, sizeof(serveraddr));serveraddr.sin_family = AF_INET;char *local_addr="127.0.0.1";inet_aton(local_addr,&(serveraddr.sin_addr));//htons(portnumber);serveraddr.sin_port=htons(portnumber);bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));listen(listenfd, LISTENQ);maxi = 0;for ( ; ; ) {//等待epoll事件的发生nfds=epoll_wait(epfd,events,20,500);//处理所发生的所有事件for(i=0;i<nfds;++i){if(events[i].data.fd==listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。

相关文档
最新文档