Redis网络模型-IO多路复用

news2024/9/20 8:53:32

Redis网络模型-IO多路复用

系统IO交互

image-20221031124722809

IO多路复用概念

文件描述符(File Descriptor):简称FD,是一个从O开始递增的无符号整数,用来关联Linux中的一个文件。在Linux中,一切皆文件,例如常规文件、视频、硬件设备等,当然也包括网络套接字(Socket)。

I0多路复用︰是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。

IO多路复用的实现方式

观前提醒

下文中经常同时出现fd和socket,fd为笼统说法,便于理解,详细解释流程时使用socket

select

select是Linux中最早的IO多路复用实现方案

linux下的select函数定义

int select (int maxfdp, 
            fd_set *readset, 
            fd_set *writeset, 
            fd_set *exceptest, 
            struct timeval *timeout);
//参数详解
nfds:select中监视的文件句柄数,一般设为要监视的文件中的最大文件号加一。
readfds:检测读是否就绪的文件描述符集合
writefds :检测写是否就绪的文件描述符集合
exceptfds:检测异常情况是否发生的文件描述符集合
(1、信包模式下伪终端主设备上从设备状态发生改变;2、流式套接字接收到了带外数据)
timeout: 设为NULL,等待直到某个文件描述符发生变化;设为00毫秒,不等待直接返回;
设为大于0的值,有描述符变化或超时时间到才返回。
select返回值:负数表示有错误发生;大于等于0,表示有n个描述符就绪。具体是哪个,
还需要用FD_ISSET遍历判定。

fd_set操作接口

FD_CLR(inr fd,fd_set* set);将描述符fd从fdset所指向的集合中移除
FD_SET(int fd,fd_set*set);将描述符fd添加到fdset所指向的集合中
FD_ZERO(fd_set *set); 将fdset指向的集合初始化为空
FD_ISSET(int fd,fd_set *set);测试描述词组set中相关fd 的位是否为真

简要图解

image-20221030225321479

详细图解

image-20221031090920282

实现步骤配合文字食用更佳

  1. CPU 执行工作队列中的进程,进程A 获得 CPU 使用权,程序执行
  2. 进程A 内部绑定了监听端口,接受远端连接,每当远端连接过来时,就建立一个代表连接的 Socket 对象
  3. 将新建的 Socket 对象添加到进程A 维护的 Socket 列表中,调用 select 时将 Socket 列表传入,由内核负责遍历 Socket 列表中的每一个 Socket,检查其是否可以操作。需注意,每次调用 select 都需要将 Socket 列表由用户进程拷贝到内核,当 Socket 列表比较大时,拷贝操作是一个不可忽视的开销,因此 Select 会限制监听 Socket 的最大数量
  4. 内核轮询 Socket 列表,如果没有任何 Socket 就绪,进程A 就需要在 select 调用处阻塞,也就是被从工作队列中移除。需要注意,进程A 还需要被添加到它所监听的每一个 Socket 的等待列表中,也就是说这里存在遍历 Socket 列表并操作 Socket 的开销
  5. 当计算机接收到外部的网络数据时,首先会经由网卡将其写入内核缓冲区,完成后网卡发出中断信号通知 CPU 有数据到达
  6. CPU 收到中断信号,执行对应的中断程序
  7. 中断程序将内核缓冲数据拷贝到对应的 socketA 的接收缓冲区(用户)
  8. socketA 接收数据完毕,中断程序将进程A 重新添加到工作队列,并将进程A 从它所监听的每一个 Socket 的等待列表中移除,此处依然存在遍历 Socket 列表并操作 Socket 的开销
  9. 进程A 再次获得 CPU 使用权后从 select 阻塞处重新执行,此时进程A 知道它所监听的 Socket 列表中存在可以读写操作的 Socket,但是并不知道到底是哪一个 Socket,此处仍然需要遍历列表才能进行下一步操作

优势

  • IO多路复用的第一阶段是使用select进程监听一批fd,一旦有fd就绪,通知recvfrom进行拷贝数据(第二阶段),即第二阶段不存在阻塞,其阻塞只存在于第一阶段,若一批fd都没有就绪,才会不可避免的阻塞进行等待,相对于阻塞式IO的recvfrom的单个调用,没有数据就阻塞,效率更高
  • 跨平台,win、linux、macOS、类unix等

缺点

  • 监听上限受文件描述符限制,最大为1024

poll

poll模式对select模式做了简单改进,但性能提升不明显

linux下的poll函数

//pollfd 中的事件类型
#define POLLIN //可读事件
#define POLLOUT //可写事件
#define POLLERR //错误事件
#define POLLNVAL //fd未打开
//pollfd结构
struct pollfd {
	int fd;//错误事件
	short int events; //要监听的事件类型:读、写、异常
    short int revents;//实际发生的事件类型
};
//poll函数
int poll(
	struct pollfd *fds,//pollfd数组,可以自定义大小
    nfds_t nfds,//数组元素个数
	int timeout //超时时间
);

实现步骤

  1. 创建pollfd数组,向其中添加关注的fd信息,数组大小自定义
  2. 调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
  3. 内核遍历fd,判断是否就绪
  4. 数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
  5. 用户进程判断n是否大于0
  6. 大于0则遍历pollfd数组,找到就绪的fd

对比select

  • select模式中的fd_set大小固定为1024,而pollfd在内核中采用链表,理论上无上限
  • 监听FD越多,每次遍历消耗时间也越久,性能反而会下降

epoll

linux下的epoll函数

struct eventpoll {
	//...
	struct rb_root rbr; //一颗红黑树,记录要监听的FD
    struct list_head rdlist; //一个链表,记录就绪的FD
    //...
};
//1.会在内核创建eventpoll结构体,返回对应的句柄epfd
int epoll_create(int size);
//2.将一个FD添加到epoll的红黑树中,并设置ep_poll_callback
//callback触发时,就把对应的FD加入到rdlist这个就绪列表中
int epoll_ctl(
	int epfd,//epoll实例的句柄
	int op,//要执行的操作,包括:ADD、MOD、DEL
    int fd,//要监听的FD
	struct epoll_event *event //要监听的事件类型:读、写、异常等
);
//3.检查rdlist列表是否为空,不为空则返回就绪的FD的数量
int epoll_wait(
	int epfd,// eventpoll实例的句柄
	struct epoll_event *events,// 空event数组,用于接收就绪的FD
    int maxevents,//events数组的最大长度
	int timeout//超时时间,-1用不超时;0不阻塞;大于0为阻塞时间
);

简要图解

image-20221031103833023

详细图解

image-20221031110021366

实现步骤配合文字食用更佳

  1. CPU 执行工作队列中的进程,进程A 获得 CPU 使用权,程序执行
  2. 进程A 内部绑定了监听端口,接受远端连接,每当远端连接过来时,就建立一个代表连接的 Socket 对象
  3. 将新建的 Socket 对象添加到进程A 创建的 epoll 对象内部的rbr红黑树结构中,也就是说进程A 监听的 Socket 集合交由内核维护。这样每次只需要将新的 Socket 对象单独拷贝到内核,开销很小,另外这个过程中也会设置回调函数到 Socket 的等待列表中
  4. epoll_wait 调用检查其内部rdlist就绪列表,如果没有任何 Socket 就绪,进程A 就需要在此处阻塞,也就是被从工作队列中移除。需要注意,进程A 此时只要被添加到 epoll 的wq等待列表中即可,不需要被添加到每一个 Socket 的等待列表
  5. 当计算机接收到外部的网络数据时,首先会经由网卡将其写入内核缓冲区,完成后网卡发出中断信号通知 CPU 有数据到达
  6. CPU 收到中断信号,执行对应的中断程序
  7. 中断程序将内核缓冲数据拷贝到对应的 socketA 的接收缓冲区
  8. socketA 接收数据完毕,中断程序将其添加到 epoll 的rdlist就绪列表
  9. socketA 数据就绪,触发步骤3设置的回调函数,唤醒进程A,将其从 epoll 的等待列表中移除。因为进程A 未被直接添加到所有 Socket 的等待列表,所以此处也就不需要将其从每个 Socket 的等待列表移除
  10. 进程A 回到工作队列,再次获得 CPU 使用权后从 epoll_wait 阻塞处重新执行,此时进程A 只需要读取 epoll 的rdlist就绪列表就能知道哪些 Socket 读写就绪,epoll中的events数组发挥作用,用于存储就绪的fd,直接操作就绪列表中 Socket 的即可,不需要再遍历查找

对比select和poll

select和poll的共同缺陷:select/poll 低效的原因之一是将 “添加 / 维护待检测任务” 和 “阻塞进程 / 线程” 两个步骤合二为一。每次调用 select 都需要这两步操作,然而大多数应用场景中,需要监视的 socket 个数相对固定,并不需要每次都修改。

epoll的优势:

  • epoll 将“添加 / 维护待检测任务” 和 “阻塞进程 / 线程” 这两个操作分开,先用 epoll_ctl() 维护等待队列,再调用 epoll_wait() 阻塞进程(解耦),效率得到了提升

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高,性能不会随监听的FD数量增多而下降

  • 每个FD只需要执行一次epoll_ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝FD到内核空间

  • 内核会将就绪的FD直接拷贝到用户空间的指定位置,用户进程无需遍历所有FD就能知道就绪的FD是谁

IO多路复用的事件通知机制

当FD有数据可读时,我们调用epoll_wait就可以得到通知。但是事件通知的模式有两种:

  • LevelTriggered: 简称LT。当FD有数据可读时,会重复通知多次,直至数据处理完成。是Epoll的默认模式。
  • EdgeTriggered:简称ET。当FD有数据可读时,只会被通知一次,不管数据是否处理完成。

例:

  1. 假设一个客户端socket对应的FD已经注册到了epoll实例中

  2. 客户端socket发送了2kb的数据

  3. 服务端调用epoll_wait,得到通知说FD就绪

  4. 服务端从FD读取了1kb数据命

  5. 回到步骤3(再次调用epoll_wait,形成循环)

其他网络模型补充

阻塞IO

阻塞IO就是两个阶段都必须阻塞等待

image-20221031124811952

非阻塞IO

非阻塞IO的用户应用会频繁发起请求等待返回成功

image-20221031124934912

信号驱动IO

信号驱动IO是与内核建立SIGlO的信号关联并设置回调,当内核有FD就绪时,会发出SIGIO信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待

image-20221031123825152

当有大量I0操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出而且内核空间与用户空间的频繁信号交互性能也较低。

异步IO

异步lO的整个过程都是非阻塞的,用户进程调用完异步API后就可以去做其它事情,内核等待数据就绪并拷贝到用户空间后才会递交信号,通知用户进程。

image-20221031123912152

由于异步IO完全不阻塞,需要控制内核并发量,并发过高容易导致崩溃

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

卷积神经网络的卷积层

文章目录卷积核正向传播反向传播参考文献附录卷积核 笔者在学会了如何运用卷积神经网路后,突然有一天萌发了很多问题,为什么要用卷积核?卷积核具体完成了什么工作?带着这些疑问,笔者开始查询资料,其中一段视…

MongoDB入门与实战-第一章-介绍

目录参考一、介绍二、概念三、预留默认库四、 MongoDB 集合五、 MongoDB 视图六、MongoDB 索引七、MongoDB ObjectIdMongoDB 性能问题定位方式参考 MongoDB 基础浅谈 一、介绍 MongoDB是为快速开发互联网Web应用而设计的数据库系统。 MongoDB的设计目标是极简、灵活、作为We…

[vue3] Tree/TreeSelect树形控件使用

✨✨个人主页:沫洺的主页 📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专…

【定语从句练习题】That 、who、whom、省略

1. 改写训练 1.I’d like to speak to the person that wrote this letter. 主 2.The tomatoes that I bought yesterday. 宾,可以去掉 3.Joe’s got a motorbike that can do 200Km an hour. 主 4.Is that the computer that doesn’t work. 主 5.Those are trous…

[操作系统笔记]处理机调度

调度算法 名称英文作业调度进程调度说明特点先来先服务First-come first-served, FCFS适用适用按作业到达先后顺序(即优先考虑等待时间最长的)非抢占式短作业优先short job first, SJF适用适用作业越短(即运行时间越短)优先级越高…

SpringBoot异常:Process finished with exit code 0 | Tomcat服务没有启动 | 无法通过浏览器访问

错误信息 启动springBoot项目后,打印信息如下 意思是:我该执行的程序已执行完毕,并正常退出。 希望提示:打印Tomcat已在8080端口启动,可以通过浏览器访问,如果是这个问题,继续向下浏览&#xf…

标准库类型string和vector

一、命名空间 std::cinstd就是命名空间, 这个的含义是 :编译器应该从操作符左侧的名字所示的作用域std中去寻找cin。 另一种方式就是在开头显式进行说明: using std::cin;这样一来后续就不用再去在每条语句中显式说明了。 需要注意的是&…

微信小程序实战 wx.showNavigationBarLoading(),下拉动画配置无效

文章目录前情提要原因分析实战解析最后前情提要 下拉刷新一定是移动端常用操作,微信小程序官方集成了下拉刷新监听函数onPullDownRefresh(),以及显示下拉动画apiwx.showNavigationBarLoading(),但是我们在初次接触这个api发现,调用该函数动画不显示&…

头歌-信息安全技术-Spectre侧信道攻击过程验证

头歌-信息安全技术-Spectre侧信道攻击过程验证一、第1关:Cache vs Memory1、编程要求2、评测代码二、第2关:基于FlushReload的侧信道实现1、编程要求2、评测代码三、第3关:Spectre预测执行1、编程要求2、评测代码四、第4关:Spectr…

【Unity】关于升级到2021.3.12之后URP编译错误的问题

前几天,我一时兴起,把Unity从2021.3.11 LTS 升级到 2021.3.12 LTS,本来以为不会有啥区别,然后意想不到的是,居然出现了编译错误: 我一开始以为这个就是我的工程设置有问题,然后我就就新…

前端面试之Vue专题

目录 前言 MVVM模式 Vue的响应式原理 路由守卫 前言 网上有许多前端八股文,但是缺少便于理解的说明和案例,需要自行查阅资料。这篇文章我就按照面试的高频题来记录自己的理解和实操。 MVVM模式 一、三者含义 M是Model,数据模型&#xf…

非项目活动的时间怎么跟踪?

会计、审计、合规和专业服务企业通常需要跟踪花费在项目和非项目上的时间以进行报告。员工可以使用8Manage工时表这样的工具来获取与项目和非项目任务相关的工作时间,并记录管理时间。 非项目时间类别确定在项目工作之外发生的不同类型的活动。你可以在工时表解决方…

【网络篇】第六篇——网络套接字编程(二)(UDP详解)

基于UDP协议的套接字程序 服务端 服务端创建套接字 服务的绑定 字符串IP VS 整数IP 运行服务器 客户端 客户端创建套接字 客户端绑定 启动客户端 本地测试 INADDR_ANY 简易的回声服务器 网络测试 基于UDP协议的套接字程序 服务端 服务端创建套接字 我们把服务…

重学Android基础系列篇(三):架构动态编程技术原理

前言 本系列文章主要是汇总了一下大佬们的技术文章,属于Android基础部分,作为一名合格的安卓开发工程师,咱们肯定要熟练掌握java和android,本期就来说说这些~ [非商业用途,如有侵权,请告知我,我会删除] DD一下: And…

基于形状的匹配提纲

关键:形状,其实就是canny找出来的线条集合 1,canny线条 2,模板的线条(基于canny) 3,高斯金字塔,加速高斯法 4,没有旋转和尺度时,匹配一个有得分的结果&am…

Linux-vim使用

目录 基本vim的基本操作: 命令模式: 光标定位: $:光标定位到行右: ^:光标定位到左: shiftgG:光标定位到底部 gg:回到顶部 nshiftg表示跳转光标到第n行 文本复制相关…

输入学生的信息学号、姓名、语文成绩、数学成绩、英语成绩,计算总分、并按总分成绩排序,再写到另一个txt文件中(python)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 题目: 代码: 1.提前写入标题栏(学号、姓名、语文、数学、英语、总分) 2.再写入学生的信息 3.读取score2.txt文件 4.…

机器学习西瓜书学习记录-第五章 神经网络

第5章 神经网络 5.1神经元模型 神经网络中最基本的成分是神经元模型。 “M-P神经元模型”,又称“阈值逻辑单元” 在模型中,神经元接收到来自n个其他神经元传递过来的输入信号,这些输入信号通过带权重的连接进行传递,神经元接收到…

分分钟让你学会栈和队列

数据结构——栈和队列 🏖️专题:数据结构 🙈作者:暴躁小程序猿 ⛺简介:双非本科大二小菜鸟一枚,希望我的博客可以对大家有所帮助 文章目录数据结构——栈和队列前言一、什么是栈?二、栈的相关概…

计算机毕设(附源码)JAVA-SSM蓟县农家乐网站

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…