EPOLL

回顾select、poll机制:

  1. select与poll本质上都是监控描述符集内的事件,返回就绪态的文件描述符个数,同个文件描述符个数去遍历集合,找出是具体哪一个文件描述符有事件发生。
  2. poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
  3. 他们的时间复杂度均为O(n)

epoll的引入

目前epell是linux大规模并发网络程序中的热门首选模型。
epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率,因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
epoll除了提供select/ poll那种IO事件的电平触发(Level Triggered)外,还提供了边沿触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
select() 和 poll() 在“醒着”的时候要遍历整个 fd 集合,而 epoll 在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的 CPU 时间。这就是回调机制带来的性能提升,时间复杂度为O(1)。

扩展

  1. 一个进程打开最大的文件描述符数量可用如下命令查看(一般为40W):
    cat /proc/sys/fs/file-max
    该数量取决于内存的大小,因为每个文件描述符在内核中都会开辟对应一个FILE结构体。
  2. 以上为最大值,而系统启动时会调用一个API对文件描述符数量进行设置,对应配置文件为:/etc/security/limits.conf
    用户可写入一下配置,soft软限制,hard硬限制。- nofile - 对应max number of open files,soft为用户自定义值,hard为用户最大定义值。

多路复用的三种机制区别可见总结篇

https://blog.csdn.net/weixin_42889383/article/details/102460106

EPOLL机制

epoll不在是通过将文件描述符保存在数组中,而是用树来管理的,用struct epoll_event 描述符封装了文件描述符以及对应的事件状态。例如当有客户连接时,初始化描述符并将其插入树中。
用树的特性的好处是:插入删除简单、遍历查找快。

API

  1. 创建一个epoll句柄,参数size用来告诉内核监听的文件描述符个数
int epoll_create(int size)size:告诉内核监听的数目
  1. 控制某个epoll监控的文件描述符上的事件:注册、修改、删除。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)参数1epfd:为epoll_creat的句柄参数2op:表示动作,用3个宏来表示:
EPOLL_CTL_ADD(注册新的fd到epfd),
EPOLL_CTL_MOD(监听fd的模式,LT或ET)
1.  对于LT为水平触发(缺省方式),同时支持block和no-block,例如监控的文件描述符集中有读事件发生,如果读数据时没有对缓冲区一次性读完,那么内核将继续在通知你去读,这种编程出错可能性小,传统的select/poll都是这种模型的代表。
2.  对于ET为边沿触发(高速工作方式),只支持no-block,如果有读事件发生,一次性未读完,那些内核将不会在通知,导致该任务变为未就绪态。
EPOLL_CTL_DEL(从epfd删除一个fd);参数3fd:指定 fd参数4event:告诉内核需要监听的事件
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};事件的状态:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭)
EPOLLOUT:表示对应的文件描述符可以写
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来
说的
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需
要再次把这个socket加入到EPOLL队列里
  1. 等待所监控文件描述符上有事件的产生,类似于select()调用。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout)events:用来从内核得到事件的集合,
maxevents:告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,
timeout:是超时时间
-1:阻塞
0:立即返回,非阻塞
>0:指定微秒
返回值:成功返回有多少文件描述符就绪,时间到时返回0,出错返回-1

EPOLL用法

阻塞模式下与select、poll相似,epoll一般用于非阻塞状态下,并且操作客户端为边沿模式。即对一个事件发送的只处理一次,不管有没有处理完。

附上代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>#include "wrap.h"#define SERV_PORT 8000
#define MAX_LOG 20
#define MAX_EPOLL 500
#define MAX_LINE 100int mz_ipv4_create_socket(void)
{int listenfd;struct sockaddr_in servaddr;//1.socketlistenfd = Socket( AF_INET, SOCK_STREAM, 0);//2.修改套接字选项,调整2MSL时间int b_reuse = 1;setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));//3.绑定IP和服务器端口号servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//4.设置监听Listen(listenfd, MAX_LOG);//5.返回套接字文件描述符return listenfd;
}int msg_handle(int sockfd)
{int bytes,len = 0;char buf[MAX_LINE];char *s = buf;int flag = 1;while(flag){bytes = recv(sockfd, s, 5, 0);if(bytes < 0){if(errno == EAGAIN){printf("no data\n");break;}perror("recv err");return -1;}s += bytes;len += bytes;if(buf[len-1] == '\n')flag = 0;}if(Write(sockfd, buf, len)<0)perror("send data err");return 0;
}int main(int argc, char *argv[])
{int listenfd, epollfd,nready,sockfd;int i = 0,ret;struct sockaddr_in clientaddr;socklen_t clientaddr_len;struct epoll_event ev, events[MAX_EPOLL];//1.创建epoll模型epollfd = epoll_create(MAX_EPOLL);if(epollfd < 0)perr_exit("epoll_create");//2.创建套接字并绑定IP与端口listenfd = mz_ipv4_create_socket();fcntl( listenfd, F_SETFL, O_NONBLOCK);//3.将服务器网络进程加入epoll树监控ev.data.fd = listenfd;ev.events = EPOLLIN;ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);if(ret < 0)perr_exit("the server insert error");//4.进入轮询监控while(1){nready = epoll_wait(epollfd, events, MAX_EPOLL, -1);if(nready < 0)perr_exit("epoll_wait");for( i = 0; i < nready; i++){//4.1服务器读事件(连接客户端)if(events[i].data.fd == listenfd){clientaddr_len = sizeof(clientaddr);sockfd = Accept(listenfd, (struct sockaddr *)&clientaddr, &clientaddr_len);ev.data.fd = sockfd;ev.events = EPOLLIN | EPOLLET;ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);printf("client[%d] connect success!\n",i);if(ret < 0)if(send(sockfd, "connect failed\n", 16, 0)<0)perror("send client err failed");continue;}else{sockfd = events[i].data.fd;ret = msg_handle(sockfd);if(ret == -2){ret = epoll_ctl(epollfd, EPOLL_CTL_DEL, sockfd, NULL);printf("client[%d] close!\n",i);Close(sockfd);}continue;}}}return 0;
}

多路复用之epoll模型相关推荐

  1. IO多路复用之epoll模型

    一.IO多路复用:一个线程监测多个IO操作 基本思想:先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或多个已经准备好进行I/O函数时才返回.函数返回时告诉进程哪个描述符已经就绪, ...

  2. LinuxI/O多路复用转接服务器——epoll模型实现

    LinuxI/O多路复用转接服务器--epoll模型实现 epoll函数 epoll函数组 epoll_create函数 epoll_ctl函数 epoll_wait函数 epoll实现实现I/O多路 ...

  3. 多路复用 I/O 模型详解, 为什么他能支持更高的并发

    阻塞 I/O 在这种 IO 模型的场景下,我们是给每一个客户端连接创建一个线程去处理它.不管这个客户端建立了连接有没有在做事(发送读取数据之类),都要去维护这个连接,直到连接断开为止.创建过多的线程就 ...

  4. linux epoll模型

    原文:http://yjtjh.blog.51cto.com/1060831/294119 Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数.Linux ...

  5. 【转】select和epoll模型的差异

    http://www.cppblog.com/converse/archive/2008/10/12/63836.html epoll为什么这么快 epoll是多路复用IO(I/O Multiplex ...

  6. 【转】Epoll模型

    Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后     首先,在Li ...

  7. select 和epoll模型区别

    1.select 和epoll模型区别 1.1.网络IO模型概述 通常来说,网络IO可以抽象成用户态和内核态之间的数据交换.一次网络数据读取操作(read),可以拆分成两个步骤:1)网卡驱动等待数据准 ...

  8. epoll模型之服务器设计

    Linux  2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数. 1.为什么select落后     首先,在L ...

  9. Linux 下 I/O 多路复用技术 epoll

    Linux 下 I/O 多路复用技术 epoll 流?I/O操作?阻塞? 解决阻塞死等待的方法 方法一:阻塞 + 多进程 / 多线程. 方法二:非阻塞 + 忙轮询. 方法三:select(与平台无关) ...

  10. 学习C++项目——select模型,poll模型和epoll模型

    学习计算机网络编程 一.思路和学习方法   本文学习于:C语言技术网(www.freecplus.net),在 b 站学习于 C 语言技术网,并加以自己的一些理解和复现,如有侵权会删除.   接下来应 ...

最新文章

  1. gym102443 D.Guess the Path
  2. 项目开发过程中的收获与思考
  3. 6年面试经验总结!让设计师提升进公司的能力!
  4. 反编译华为U8825D“framework-res.apk”出现的错误提示(1)
  5. 1008. 数组元素循环右移问题 (20)
  6. 游标定位:Cursor类
  7. Eclipse3.6.2 64位启动报“Failed to load the JNI shared library”错的解决方法
  8. Centos 手工创建新用户
  9. 豆瓣的jQuery使用技巧
  10. c语言宏定义数组_利用数组处理批量数据 C语言程序编写定义与利用数组技巧全归纳...
  11. ask调制流程图_ASK调制及相干解调电路设计.doc
  12. JS定时器原理及案例
  13. 历年(2015-2018)英语六级翻译真题及参考答案
  14. 备案的是域名还是服务器?
  15. Typo3 CVE-2019-12747 反序列化漏洞分析
  16. jetson nano的处理器架构
  17. Arcpy批量导出shp文件属性表——使用arcpy.da.SearchCursor函数
  18. 全差分运算放大器ADA4930的分析(1)
  19. 工作了,才知道......
  20. 剑指offter 数据结构之数组

热门文章

  1. 手动安装Ubuntu 16.04无线wifi驱动,任意网卡型号均可
  2. jtag接口_一份很好的JTAG完全解读资料
  3. cread(creade吹风机)
  4. [网络安全自学篇] 四.实验吧CTF实战之WEB渗透和隐写术解密
  5. 我要写整个中文互联网界最牛逼的JVM系列教程 | 「JVM与Java体系架构」章节:官方规范下载与参考书目
  6. Python语言的起源与发展
  7. android singleTask使用情况,场景分析
  8. QT 车牌号正则验证
  9. Kindle PaperWhite 3 5.8.10越狱成功!
  10. 《LDA数学八卦》读后笔记