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的更多相关文章

  1. 非阻塞套接字编程, IO多路复用(epoll)

    非阻塞套接字编程: server端 import socket server = socket.socket() server.setblocking(False) server.bind(('', ...

  2. IO多路复用之epoll总结

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  3. IO多路复用之epoll

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  4. 【python】-- IO多路复用(select、poll、epoll)介绍及实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

  5. IO多路复用(select、poll、epoll)介绍及select、epoll的实现

    IO多路复用(select.poll.epoll)介绍及select.epoll的实现 IO多路复用中包括 select.pool.epoll,这些都属于同步,还不属于异步 一.IO多路复用介绍 1. ...

  6. 异步、非阻塞和IO多路复用总结

    Nginx是并发处理框架的代表者,很多后台业务都会放在Nginx容器中运行,以实现高吞吐,而Nginx能够支持高并发也是由于使用了异步非阻塞处理模型,本文将用通俗的话讲解异步.同步.阻塞.非阻塞的区别 ...

  7. nginx 多进程 + io多路复用 实现高并发

    一.nginx 高并发原理 简单介绍:nginx 采用的是多进程(单线程) + io多路复用(epoll)模型 实现高并发 二.nginx 多进程 启动nginx 解析初始化配置文件后会 创建(for ...

  8. 聊聊redis单线程为什么能做到高性能和io多路复用到底是个什么鬼

    1:io多路复用epoll  io多路复用简单来说就是一个线程处理多个网络请求 我们知道epoll in 的事件触发是可读了,这个比较好理解,比如一个连接过来,或者一个数据发送过来了,那么in事件就触 ...

  9. 聊聊IO多路复用之select、poll、epoll详解

    本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538922&idx=1&sn=e6b436ef ...

随机推荐

  1. echarts基本使用与注意事项

    npm 安装echarts npm install echarts -D 使用流程 1. 引入echarts,并配置成全局方法 vue2 import * as echarts from 'echar ...

  2. JAVA IDEA连接mysql遇到的问题

    Mysql-connector-java驱动问题 因为缺乏驱动而无法成功连接数据库 下载驱动(教程) 相关网址 安装驱动 简单图示

  3. python3 获取函数变量

    Python 3.8可以使用f字符串调试功能: 1 test_dict = {1: "1", 2: "2", 3: "3"} 2 print ...

  4. go-websocket服务端/客户端

    目录 websocket 服务端 客户端 websocket websocket.Upgrader升级为websocket协议 服务端 package main import ( "fmt& ...

  5. 使用WebDriverManager实现自动获取浏览器驱动程序

    原理: 自动到指定的地址下载相应的浏览器驱动保存到缓存区 ~/.cache/selenium 痛点: 解决因Chrome浏览器升级,driver需要同步升级,要重新下载驱动的问题 区别: 传统方式 需 ...

  6. nodejs + typescript + koa + eslint + typescript eslint + prettier + webstorm

    ESLint 安装 yarn add -D eslint 生成配置文件 yarn eslint --init cli 选项 How would you like to use ESLint? To c ...

  7. SM3和Blake

    在此给出SM3和Blake的对比 哈希函数 哈希算法 (Hash Algorithm) 是将任意长度的数据映射为固定长度数据的算法,也称为消息摘要.一般情况下,哈希算法有两个特点, 一是原始数据的细微 ...

  8. 为什么 Redis 要有哨兵机制?

    作者:小林coding 计算机八股文刷题网站:https://xiaolincoding.com 大家好,我是小林. 这次聊聊,Redis 的哨兵机制. 提纲 为什么要有哨兵机制? 在 Redis 的 ...

  9. ASCII&Base64

    ASCII https://zh.wikipedia.org/wiki/ASCII American Standard Code for Information Interchange,美国信息交换标 ...

  10. 102_Power Pivot DAX 排名后加上总排名数

    焦棚子的文章目录 请点击下载附件 1.背景 每次写rank的时候,有了排名就可以了,排名1,2,3,4,5这样不是很清晰吗?但是中国式报表的老板们说你能不能在排名后面加一个总排名数呢,就像1/5,2/ ...