epoll:

Linux下性能最高的多路转接模型

epoll 有3个相关的系统调用.

epoll_create

功能:创建epoll,在内核中创建eventpoll结构体,size决定了epoll最多监控多少个描述符,在Linux2.6.8之后被忽略,但是必须>0。返回一个文件描述符,作为epoll的操作句柄

struct eventpoll{...rb_root rbr(红黑树)...struct list_head rdlist(双向链表)...
}
int epoll_create(int size)

创建一个epoll的句柄.

  • 自从linux2.6.8之后,size参数是被忽略的.
  • 用完之后, 必须调用close()关闭

epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

功能:对内核中的eventpoll 结构体进行操作:epoll采用事件结构方式对描述符进行事件监控;用户定义struct epoll_event描述符事件结构信息;将事件信息可以拷贝到内核添加到eventpoll结构体中的红黑数中

参数说明
  • 它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型.
  • 第一个参数是epoll_create()的返回值(epoll的句柄).
  • 第二个参数表示动作,用三个宏来表示. 、
  • 第三个参数是需要监听的fd.
  • 第四个参数是告诉内核需要监听什么事. 描述符对应的事件结构信息
第二个参数的取值:
  • EPOLL_CTL_ADD :注册新的fd到epfd中;向红黑数中添加描述符的监控事件结构信息event
  • EPOLL_CTL_MOD :修改已经注册的fd的监听事件;修改描述符在红黑数中的对应事件结构信息event
  • EPOLL_CTL_DEL :从epfd中删除一个fd,从红黑数中移除描述符的监控事件结构信息event

struct epoll_event结构如下

struct epoll_event {
uint32_t events; /* 用户对描述符进行监控的事件 */
epoll_data_t data; /* User data variable */
};typedef union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; } epoll_data_t;

events可以是以下几个宏的集合:

  1. EPOLLIN : 表示对应的文件描述符可以读 (包括对端SOCKET正常关闭);
  2. EPOLLOUT : 表示对应的文件描述符可以写;
  3. EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来);
  4. EPOLLERR : 表示对应的文件描述符发生错误;
  5. EPOLLHUP : 表示对应的文件描述符被挂断;
  6. EPOLLET : 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的.
  7. EPOLLONESHOT:只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要 再次把这个socket加入到EPOLL队列里.

epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)
  • epfd: epoll 操作句柄
  • events:epoll_event 事件结构信息数组的结点数量
  • maxevents : epoll_event事件结构信息数组的结点数量
  • timeout:epoll_wait 监控的等待超时时间
  • 返回值:<0----监控出错 ==0----监控超时 >0----就绪的描述符事件个数
    收集在epoll监控的事件中已经发送的事件
  1. 参数events是分配好的epoll_event结构体数组.
  2. epoll将会把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这个 events数组中,不会去帮助我们在用户态中分配内存).
  3. maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size.
  4. 参数timeout是超时时间 (毫秒,0会立即返回,-1是永久阻塞).
  5. 如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时, 返回小于0表示函 数失败

epoll_wait 会将就绪的描述符对应事件结构信息拷贝到events结构数组中;相当于直接告诉用户哪个描述符就绪;用户直接就从epoll_event 结构体数组中取出信息,对描述符直接进行相应事件操作

epoll监控流程

  1. epoll对描述符的事件监控是一个异步操作;epoll_wait发起调用,让操作系统对描述符进行相应事件监控
  2. 操作系统对每个要监控的描述符都定义了就绪事件回调函数;当描述符相应事件就绪的时候,触发事件,调用回调函数(将描述符事件结构信息指针添加到eventpoll的双向链表中)
  3. 但是epoll_wait并没有直接返回(是一个阻塞操作),每隔一会就看一下eventpoll中双向链表是否为空;来判断是否有描述符就绪;若为空,则没有描述符就绪;则等待一会,重新查看
  4. 若双向链表不为空------表示有描述符事件就绪;将这个描述符对应的事件结构信息,拷贝到epoll_wait传入的事件结构数组中后调用返回。

epoll工作原理

struct eventpoll{...rb_root rbr(红黑树)...struct list_head rdlist(双向链表)...
}

当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成 员与epoll的使用方式密切相关.

  • 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来 的事件.
  • 这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插 入时间效率是lgn,其中n为树的高度).
  • 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时 会调用这个回调方法.
  • 这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中.
  • 在epoll中,对于每一个事件,都会建立一个epitem结构体.
  • 当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem 元素即可.
  • 如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度 是O(1).
/*                                                                                                                                                                                                               * 这个程序完成epoll接口的基本封装* bool Init()* bool Add(TcpSocket &sock)* bool Del(TcpSocket &sock)* bool Wait(std::vector<TcpSocket>&list,int timeout_msec)*/#include<iostream>
#include<vector>
#include<sys/epoll.h>
#include"tcpsocket.hpp"#define MAX_EVENTS 10
class Epoll
{public:bool Init(){//int epoll_create(int size)_epfd = epoll_create(1);if(_epfd < 0){ perror("epoll create error");return false;}   return true;}   bool Add(TcpSocket &sock){//int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) int fd = sock.GetFd();struct epoll_event ev; ev.data.fd = fd; ev.events = EPOLLIN;int ret = epoll_ctl(_epfd, EPOLL_CTL_ADD, fd , &ev);if(ret < 0){ perror("epoll add error");return false;}   return true;}   bool Del(TcpSocket &sock){int fd = sock.GetFd();int ret =epoll_ctl(_epfd, EPOLL_CTL_DEL, fd,NULL);if(ret < 0){perror("epoll del error");return false;}return true;}bool Wait(std::vector<TcpSocket>&list,int timeout_msec = 3000){//int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout) struct epoll_event evs[MAX_EVENTS];int nfds = epoll_wait(_epfd, evs, MAX_EVENTS, timeout_msec);if(nfds < 0){perror("epoll wait error");}else if(nfds == 0){std::cerr<<"epoll wait timeout";return false;}for(int i =0 ; i < nfds; i++ ){TcpSocket sock;sock.SetFd(evs[i].data.fd);list.push_back(sock);}return true;}private:int _epfd;};int  main()
{TcpSocket lst_sock;CHECK_RET(lst_sock.Socket());CHECK_RET(lst_sock.Bind("0.0.0.0",9000));CHECK_RET(lst_sock.Listen());Epoll epoll;CHECK_RET(epoll.Init());CHECK_RET(epoll.Add(lst_sock));while(1){std::vector<TcpSocket>list;bool ret = epoll.Wait(list);if(ret == false){            continue;}for(int i =0 ;i < list.size();i++){if(lst_sock.GetFd() == list[i].GetFd()){TcpSocket cli_sock;std::string cli_ip;uint16_t cli_port;ret = lst_sock.Accept(cli_sock,cli_ip,cli_port);if(ret == false){continue;}epoll.Add(cli_sock);}else{std::string buf;ret = list[i].Recv(buf);if(ret == false){//接收出错epoll.Del(list[i]);list[i].Close();continue;}std::cout<< "client-say:"<< buf <<std::endl;}}}lst_sock.Close();return 0;
}

epoll事件触发方式:

水平触发–EPOLLT/边缘触发EPOLLET

水平触发方式

可读事件就绪:接受缓冲区数据大小,大于低水位标记(默认1字节)
可写事件就绪:发送缓冲区中空闲大小,大于低水位标记(默认1字节)
只要接受/发送缓冲区中数据/剩余空间大小大于低水平标记就会一直触发事件

边缘触发方式

可读事件就绪:接受缓冲区中,只有新数据到来的时候才会触发一次
可写事件就绪:发送缓冲区中,只有从剩余空间大小从0变为大于0的时候才会触发

注意事项

边缘触发方式中,只有新数据到来的时候,可读事件才会触发一次
需要用户在这一次事件触发中将缓冲区中的数据全部读取完毕(循环读,直到不能读为止)
但是套接字默认recv没有数据的时候会阻塞;为了避免循环读取数据导致程序流程因为阻塞而无法继续推进,因此需要将描述符设置为非阻塞

fcntl
int fcntl(int fd,int cmd, .../*arg*/);fd : 要设置的描述符cmd : 对描述符要进行的操作F_SETFL 通过arg参数设置描述符属性状态F_GETFL 返回描述符的属性状态信息 ,arg被忽略arg:要设置的描述符属性状态信息O_NONBLOCK  将描述符设置为非阻塞

epoll优缺点分析

  1. epoll采用事件结构方式对描述符进行监控,简化了select集合操作的流程
  2. epoll描述符监控数量无上限
  3. 每个epoll监控的描述符事件信息,只需要向内核拷贝一次
  4. epoll_wait使用异步阻塞操作在内核中完成事件监控;事件监控是操作系统通过事件回调的方式就绪描述符事件信息添加到双向链表中;而epoll_wait只是每隔一段时间看一下双向链表是否为空判断是否有描述符就绪(并非轮询遍历)性能不会随着描述符增多而降低
  5. epoll直接通过epoll_wait传入的时间结构数组向用户返回就绪的事件信息;可以直接告诉用户哪些描述符就绪,不需要用户进行空遍历查找

缺点

  1. 不能跨平台

IO多路转接模型的适用场景

对大量描述符进行监控,但是同一时间只有少量描述符活跃的场景

IO多路转接模型-----epoll相关推荐

  1. IO多路转接之epoll

    IO多路转接之epoll 文章目录 IO多路转接之epoll 一.epool 二.基于epoll实现服务器(LT) 三.**基于epoll实现服务器(LT)** 一.epool 是为处理大批量句柄而作 ...

  2. IO多路转接模型----(select的模型,select的优缺点,poll的模型,poll的优缺点)

    IO多路转接模型:select/poll/epoll 对大量描述符进行事件监控(可读/可写/异常) select模型 用户定义描述符的事件监控集合 fd_set(这是一个位图,用于存储要监控的描述符) ...

  3. 高级IO--1 ---(五种典型IO,阻塞IO,非阻塞IO,信号驱动IO,异步IO, IO多路转接)

    高级IO: 五种典型IO: 阻塞IO/非阻塞IO/信号驱动IO/异步IO/IO多路转接 IO多路转接模型:select/poll/epoll 五种典型IO 阻塞IO IO操作的流程:等待IO操作条件具 ...

  4. Linux征途——多路转接模型

    文章目录 1. 简介 2. select 3. poll 3. epoll 3.1 相关系统调用 3.2 工作流程 3.3 回调机制 3.3.1 水平触发Level Triggered 3.3.2 边 ...

  5. IO多路转接 ——— select、poll、epoll

    文章目录 I/O多路转接之select select初识 select函数 socket就绪条件 select基本工作流程 select服务器 select的优点 select的缺点 select的适 ...

  6. select poll epoll 高效IO 多路转接

    目录 五种常见IO模型 高效IO的概念 阻塞 vs 非阻塞 非阻塞IO fcntl函数 I/O多路转接之select 初识select select函数原型 select操作接口 tcp_server ...

  7. epoll(eventpoll)是干嘛的?IO多路转接技术(相较select、poll的优点)

    首先我们要知道epoll是用来干什么的(定义):epoll是一种IO多路转接技术,在LINUX网络编程中,经常用来做事件触发,即当有特定事件到来时,能够检测到,而不必阻塞进行监听,基于事件驱动的IO多 ...

  8. 多路转接模型多路复用模型

    多路转接模型&多路复用模型 功能: 同时监控大量描述符,然后逐个针对就绪的描述符进行处理: 针对大量描述符进行IO事件监控,让进程可以只针对就绪的描述符进行IO操作,提高IO效率,避免针对未就 ...

  9. 网络编程(三)TCP IO多路转接服务器编程(select)

    同系列文章: 1, 文章目录 一,select知识引入 二,select基本概念 2.1 select概念理解前先总结一下通信过程(便于后文理解select) 2.2 select函数的用法 2.2. ...

最新文章

  1. 近十年和近三年智能车竞赛国赛奖项在各赛区的分布分析
  2. Linux终端中设置vi编辑命令
  3. 【渝粤教育】国家开放大学2018年春季 0508-21T影视特技及后期合成 参考试题
  4. c语言寻找最小路集,寻找一个准确的方法来用C语言编写微基准小的代码路径++和在Linux / OSX上运行...
  5. python文献检索工具与技巧答案_短文本分析----基于python的TF-IDF特征词标签自动化提取...
  6. Java==与equals方法的区别
  7. mysql 一张表的数据插入另一张表的sql语句
  8. 区块链开源框架 HyperLedger Fabric 学习思路分享
  9. PCIE协议(原版) 免费分享
  10. 分形之皇冠(Crown)
  11. DXP2004/Altium Desinger 自己画元器件和封装,及注意事项
  12. 【TcaplusDB知识库】读取数据示例(TDR表)
  13. 我在汇才的亲身经历与感受
  14. 如果宁静是Oracle,那万茜,张雨绮,黄圣依 是什么?(附姐姐信息表)
  15. 技术沙龙|原来落地AI应用是这么回事儿!
  16. 解决STM8S103K3T6打开BEEP功能遇到的无法操作BEEP_CSR的问题
  17. Android 自定义注解处理器详解
  18. php常用系统函数库,PHP常用函数大全
  19. 计算机excel求四分位数,如何在Excel中计算一组数字的百分位数和四分位数?
  20. android 寺库trytry_美图与寺库、TryTry三方达成战略合作,美图美妆App将由TryTry运营...

热门文章

  1. docker 出现 Error response from daemon
  2. C#线程 ---- 线程同步详解
  3. jira java接口生成问题
  4. Python练习-迭代器-模拟cat|grep文件
  5. 脑子越来越不好使,文字越来越像驮shi
  6. mybatis06 增删改差 源码
  7. C++细节系列(零):零散记录
  8. [Android] (在ScrollView里嵌套view)重叠view里面的onTouchEvent的调用方法
  9. mvc的Controller返回值类型ActionResult详解
  10. postgresql存图片字段类型_PostgreSQL让人着迷的多态性,另辟蹊径省时又省力