epoll_create、epoll_ctl、epoll_wait、close

在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,linux/posix_types.h头文件有这样的声明:
#define__FD_SETSIZE   1024
         表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本

epoll的接口非常简单,一共就三个函数: 1.创建epoll句柄    int epfd = epoll_create(intsize);                                                           

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。 函数声明:int epoll_create(int size) 该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同 2.将被监听的描述符添加到epoll句柄或从epool句柄中删除或者对监听事件进行修改。

函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。 参数: epfd:由 epoll_create 生成的epoll专用的文件描述符; op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除
fd:关联的文件描述符; event:指向epoll_event的指针; 如果调用成功返回0,不成功返回-1

int epoll_ctl(int epfd, intop, 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,
          第四个参数是告诉内核需要监听什么事件,structepoll_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队列里。

如:
struct epoll_event ev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

3.等待事件触发,当超过timeout还没有事件触发时,就超时。

   int epoll_wait(int epfd, struct epoll_event * events, intmaxevents, int timeout);     等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大(数组成员的个数),这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。
    该函数返回需要处理的事件数目,如返回0表示已超时。
    返回的事件集合在events数组中,数组中实际存放的成员个数是函数的返回值。返回0表示已经超时
函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:等待I/O事件发生的超时值(单位我也不太清楚);-1相当于阻塞,0相当于非阻塞。一般用-1即可
返回发生事件数。
epoll_wait运行的原理是
等侍注册在epfd上的socket fd的事件的发生,如果发生则将发生的sokct fd和事件类型放入到events数组中。
并 且将注册在epfd上的socket fd的事件类型给清空,所以如果下一个循环你还要关注这个socket fd的话,则需要用epoll_ctl(epfd,EPOLL_CTL_MOD,listenfd,&ev)来重新设置socket fd的事件类型。这时不用EPOLL_CTL_ADD,因为socket fd并未清空,只是事件类型清空。这一步非常重要。
 
epoll_wait范围之后应该是一个循环,遍历所有的事件:
 
nfds = epoll_wait(epfd,events,20,500);  
{
for(n=0;n<nfds;++n)
{
if(events[n].data.fd==listener)
{
//如果是主socket的事件的话,则表示
//有新连接进入了,进行新连接的处理。
client=accept(listener,(structsockaddr*)&local,&addrlen);
if(client<0)
{
perror("accept");
continue;
}
setnonblocking(client);//将新连接置于非阻塞模式
ev.events=EPOLLIN|EPOLLET;//并且将新连接也加入EPOLL的监听队列。
//注意,这里的参数EPOLLIN|EPOLLET并没有设置对写socket的监听,
//如果有写操作的话,这个时候epoll是不会返回事件的,如果要对写操作
//也监听的话,应该是EPOLLIN|EPOLLOUT|EPOLLET
ev.data.fd=client;
if(epoll_ctl(kdpfd,EPOLL_CTL_ADD,client,&ev)<0)
{
//设置好event之后,将这个新的event通过epoll_ctl加入到epoll的监听队列里面,
//这里用EPOLL_CTL_ADD来加一个新的epoll事件,通过EPOLL_CTL_DEL来减少一个
//epoll事件,通过EPOLL_CTL_MOD来改变一个事件的监听方式。
fprintf(stderr,"epollsetinsertionerror:fd=%d0,client);
return-1;d
}
}
elseif(event[n].events&EPOLLIN)
{
//如果是已经连接的用户,并且收到数据,
//那么进行读入
intsockfd_r;
if((sockfd_r=event[n].data.fd)<0)continue;
read(sockfd_r,buffer,MAXSIZE);
//修改sockfd_r上要处理的事件为EPOLLOUT
ev.data.fd=sockfd_r;
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd_r,&ev)
}
elseif(event[n].events&EPOLLOUT)
{
//如果有数据发送
intsockfd_w=events[n].data.fd;
write(sockfd_w,buffer,sizeof(buffer));
//修改sockfd_w上要处理的事件为EPOLLIN
ev.data.fd=sockfd_w;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd_w,&ev)
}
do_use_fd(events[n].data.fd);
}
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

从man手册中,得到ET和LT的具体描述如下
EPOLL事件有两种模型:
Edge Triggered(ET)       //高速工作方式,错误率比较大,只支持no_block socket (非阻塞socket)
LevelTriggered(LT)       //缺省工作方式,即默认的工作方式,支持blocksocket和no_blocksocket,错误率比较小。

假如有这样一个例子:(LT方式,即默认方式下,内核会继续通知,可以读数据,ET方式,内核不会再通知,可以读数据)
1.我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)......

Linux epoll机制的更多相关文章

  1. linux epoll机制对TCP 客户端和服务端的监听C代码通用框架实现

    1 TCP简介 tcp是一种基于流的应用层协议,其“可靠的数据传输”实现的原理就是,“拥塞控制”的滑动窗口机制,该机制包含的算法主要有“慢启动”,“拥塞避免”,“快速重传”. 2 TCP socket ...

  2. 剖析epoll机制

    剖析epoll机制 Linux epoll机制; 写这篇文章的原因是, 上次百度面试被问到一个事件怎么添加到epoll的双向链表中的; 这个问题比较深入, 涉及到内核的实现问题, 今天就来理解一下; ...

  3. 深入理解Linux的I/O复用之epoll机制

    0.概述 通过本篇文章将了解到以下内容: I/O复用的定义和产生背景 Linux系统的I/O复用工具演进 epoll设计的基本构成 epoll高性能的底层实现 epoll的ET模式和LT模式 epol ...

  4. 源码剖析Linux epoll实现机制及Linux上惊群

    转载:https://blog.csdn.net/tgxallen/article/details/78086360 看源码是对一个技术认识最直接且最有效的方式了,之前用Linux Epoll做过一个 ...

  5. Server Develop (六) Linux epoll总结

    Linux  epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低效率.因为在内核中的sele ...

  6. 【Unix环境编程】select、poll、epoll机制的联系与区别

    在linux设计并发网络程序,主要有如下几种模型:Apache模型(Process Per Connection, PPC).TPC(Thread Per Connection)模型,select机制 ...

  7. Linux Epoll介绍和程序实例

    Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...

  8. Linux epoll总结

    Linux epoll总结 Linux  epoll epoll是Kernel 2.6后新加入的事件机制,在高并发条件下,远优于select.epoll最大的好处在于它不会随着监听fd数目的增长而降低 ...

  9. Windows完成端口与Linux epoll技术简介

    收藏自:http://www.cnblogs.com/cr0-3/archive/2011/09/09/2172280.html WINDOWS完成端口编程1.基本概念2.WINDOWS完成端口的特点 ...

随机推荐

  1. Python项目读取配置的几种方式

    1. 将配置写在Python文件中 配置文件(config.py 或 settings.py) 通常放置在程序源代码的目录,方便引用 配置文件 # settings.py class Config(o ...

  2. Python的虚拟环境

    Python自带env # 新建虚拟环境 python -m venv env_name # 激活虚拟环境 cd env_name cd Scripts activate # 退出虚拟环境 # 到达虚 ...

  3. windows下创建启动脚本bat

    最主要是运用bat命令. call执行命令 比如 启动solr的服务,以前要进去solr的目录,然后bin/solr start    这样很麻烦.可以写个脚本放到桌面. call %CATALINA ...

  4. 基于ionic4、cordova搭建android开发环境

    前颜(yan)最近公司有一个项目需求是利用h5进行跨平台开发,这里所说的跨平台开发指的是:将h5代码利用某种方式或工具环境进行打包,最后生成android的apk以及ios的ipa. 本文只讲解and ...

  5. springMVC中 @RequestParam和@RequestBody的区别

    首先,不可以同时传进@RequestParam和@RequestBody,好像可以传进两个@RequestParam 如果不加@requestparam修饰,相当于 加上@requestparam且各 ...

  6. 调用腾讯、百度翻译API,实现游戏机翻通用程序

    最近玩了款steam独立游戏,没中文,只能自己汉化了,用腾讯跟百度的API实现了一个通用的机翻程序(只需要导入JSON文本), 同样,比较懒,还没写,先占坑

  7. Codeforces Round #545 (Div. 2) C. Skyscrapers 离散化+贪心

    题目链接 给你一个n∗m的矩阵res,让你输出一个n∗m的矩阵a,这个矩阵满足:给你一个n*m的矩阵res,让你输出一个n*m的矩阵a,这个矩阵满足:给你一个n∗m的矩阵res,让你输出一个n∗m的矩 ...

  8. 拆系数FFT及其部分优化

    模拟考某题一开始由于校内OJ太慢直接拆系数FFT跑不过 后来被神仙婊了一顿之后发现复杂度写炸了改了改随便过 模版题:任意模数NTT 三模数NTT 常数巨大,跑的极慢 拆系数FFT 原理是对于两个多项式 ...

  9. 一篇文让你看懂NB-IoT、LoRa、eMTC、Sigfox及ZigBee的应用场景【转】

    转自:https://blog.csdn.net/nicholas_dlut/article/details/81051269

  10. Python 官方中文教程(简)

    Python 官方教程 前言 这是一次系统学习Python官方教程的学习笔记 整个教程一共16章, 在学习过程中记录自己不知道的和一些重要的知识, 水平有限, 请指正. Python3.7 官方教程. ...