IO多路复用epoll
0 why: 问题来源
0.1 网络编程流程
//创建socket
int s = socket(AF_INET, SOCK_STREAM, 0);
//绑定IP地址和端口号port
bind(s, ...)
//监听客户端连接
listen(s, ...)
//接受客户端连接
int c = accept(s, ...)
//接收客户端数据
recv(c, ...);
//处理数据
operation(...)
0.2 内核接收网络数据过程
创建socket时,操作系统会创建一个由文件系统管理的socket对象。这个socket对象包含了发送缓冲区、接收缓冲区、等待队列等成员。等待队列指向所有需要等待该socket事件的进程。

新的文件描述符fd都会插入等待队列中,等到有数据到来时,等待序列会唤醒一个进程来处理数据。
0.3 问题来源
如何同时监视多个socket的数据?
1 解决方案之select模式
预先传入一个socket列表,如果列表中的socket都没有数据,挂起进程,直到有一个socket收到数据,唤醒进程。
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int fds[] = ;//存放需要监听的socket
while(1){
int n = select(..., fds, ...)
for(int i=0; i < fds.count; i++){
if(FD_ISSET(fds[i], ...)){
//fds[i]的数据处理
}
}
}
这里需要注意的问题是:select查看是否有数据输入,需要进行遍历所有的socket;而在进程唤醒后,进程一脸懵逼,只知道有数据来了,却不知道是谁的数据,因此需要再次进行一次遍历,找到数据来自于哪个socket。这种多次遍历,每次都要将整个fds列表传递给内核,开销很大,因此我们需要改进一下。
2 what: 解决方案之epoll模式
epoll模式相比于select模式,最大的改进在于增加了一个中间环节“就绪列表”,还有就是分离了“socket插入到等待列表”和“阻塞等待事件到来”这两个过程。
2.1 功能分离
每次调用select都需要这两步操作,然而大多数应用场景中,需要监视的socket相对固定,并不需要每次都修改。epoll将这两个操作分开,先用epoll_ctl维护等待队列,再调用epoll_wait阻塞进程。
注意:epoll_wait方法不是使用循环的方式看是否有就绪时间,而是epoll_wait()一直阻塞直到:fd产生事件 / 被信号处理函数打断 / 超时。

int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中
while(1){
int n = epoll_wait(...)
for(接收到数据的socket){
//处理
}
}
2.2 增加中间环节“就绪列表”
select低效的另一个原因在于程序不知道哪些socket收到数据,只能一个个遍历。如果内核维护一个“就绪列表”,引用已就绪数据的socket,就能避免遍历。

3 how: 如何用?
3.1 创建epoll
int epoll_create(int size);
在最初的epoll_create()实现中,size参数将调用者希望添加到的文件描述符的数量告知内核。epoll实例。内核使用该信息作为内部数据结构初始分配空间的提示,事件。 (如果有必要,如果调用方的使用超出了大小提示,内核将分配更多空间。)如今,此提示不再必需(内核无需提示即可动态调整所需数据结构的大小),但是大小必须仍大于零,以便当新的epoll应用程序在较旧的内核上运行时,请确保向后兼容。
3.2 操作事件
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
向 epfd 对应的内核epoll 实例添加、修改或删除对 fd 上事件 event 的监听。op 可以为 EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL 分别对应的是添加新的事件,修改文件描述符上监听的事件类型,从实例上删除一个事件。
3.3 监听事件
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
当 timeout 为 0 时,epoll_wait 永远会立即返回。而 timeout 为 -1 时,epoll_wait 会一直阻塞直到任一已注册的事件变为就绪。当 timeout 为一正整数时,epoll 会阻塞直到计时 timeout 毫秒终了或已注册的事件变为就绪。因为内核调度延迟,阻塞的时间可能会略微超过 timeout 毫秒。
4 参考
https://www.cnblogs.com/Hijack-you/p/13057792.html
https://www.agedcat.com/programming_language/cpp/525.html
https://blog.csdn.net/zhoumuyu_yu/article/details/112472419
IO多路复用epoll的更多相关文章
- 非阻塞套接字编程, IO多路复用(epoll)
非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...
- IO多路复用之epoll总结
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- IO多路复用之epoll
1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...
- 【python】-- IO多路复用(select、poll、epoll)介绍及实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- IO多路复用(select、poll、epoll)介绍及select、epoll的实现
IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...
- 异步、非阻塞和IO多路复用总结
Nginx是并发处理框架的代表者,很多后台业务都会放在Nginx容器中运行,以实现高吞吐,而Nginx能够支持高并发也是由于使用了异步非阻塞处理模型,本文将用通俗的话讲解异步.同步.阻塞.非阻塞的区别 ...
- nginx 多进程 + io多路复用 实现高并发
一.nginx 高并发原理 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二.nginx 多进程 启动nginx 解析初始化配置文件后会 创建(for ...
- 聊聊redis单线程为什么能做到高性能和io多路复用到底是个什么鬼
1:io多路复用epoll io多路复用简单来说就是一个线程处理多个网络请求 我们知道epoll in 的事件触发是可读了,这个比较好理解,比如一个连接过来,或者一个数据发送过来了,那么in事件就触 ...
- 聊聊IO多路复用之select、poll、epoll详解
本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538922&idx=1&sn=e6b436ef ...
随机推荐
- Intellij IDEA 高效使用教程 (插件,实用技巧) 最好用的idea插件大全
安装好Intellij idea之后,进行如下的初始化操作,工作效率提升十倍. 一. 安装插件 1. Codota 代码智能提示插件 只要打出首字母就能联想出一整条语句,这也太智能了,还显示了每条语句 ...
- Day 006:PAT练习--1005 Spell It Right (20 分)
上星期一直在写报告乱七八糟的,从今天开始不刷乙级的了,还是多刷甲级进步来得快一点! 显而易见,该题的关键在于将输入之和的每一位从高到低输出,这里我们发现题意中的输入数的范围为0-10^100,显然我们 ...
- TCP 连接的建立 & 断开
TCP 连接的建立过程 一开始,客户端和服务端都处于 close 状态. 先是服务端监听某个端口,此时服务端处于 listen 状态. 这个时候客户端就可以发送连接请求报文了. 第一次握手 客户端会主 ...
- MVC 的dao层、service层和controller层
1.dao层 dao层主要做数据持久层的工作, 负责与数据库进行联络的一些任务都封装在此 ,dao层的设计 首先 是设计dao层的接口,然后在Spring的配置文件中定义此接口的实现类,然后就可以再模 ...
- 搭建PWN学习环境
环境清单 系统环境 Ubuntu22.04 编写脚本 pwntools ZIO 调试 IDA PRO gdb pwndbg ROP工具 checksec ROPgadget one_gadget Li ...
- fedora使用root超级用户
sudo -i可以使当前用户变成root帐号. 这样就不用一遍一遍的输sudo 了! 原来用sudo su也可以.
- 手脱MoleBox(2.3.3-2.6.4)
1.查壳 2.找到OEP 对第二个Call使用ESP定律,再跳转后的位置进入第一个Call,这里就是OEP了,在这里直接dump的话会失败,那是因为MoleBox壳对IAT进行二次跳转,我们先在OEP ...
- 程序包 applets.user.service.UserService 不存在-2022新项目
一.问题由来 接上一篇文章使用maven进行打包时报中文乱码错误,经过多次尝试后最终解决问题,显示出真正的错误信息如下: 程序包 applets.user.service.UserService 不存 ...
- 栈在go语言中实现,及解决388.文件的最长绝对路径的思路
今天在LeetCode刷每日一题,遇到了388. 文件的最长绝对路径的思路,这道题让我想到了系统的目录是栈结构,果然在题解中找到了栈的解法(暴力半天没出来,跑去看题解了QWQ). 所以我就捎带复习了一 ...
- 535. Encode and Decode TinyURL - LeetCode
Question 535. Encode and Decode TinyURL Solution 题目大意:实现长链接加密成短链接,短链接解密成长链接 思路:加密成短链接+key,将长链接按key保存 ...