Redis——网络模型之IO讲解

news2025/4/20 4:55:57

目录

前言

1.用户空间和内核空间

1.2用户空间和内核空间的切换

1.3切换过程

2.阻塞IO

3.非阻塞IO

4.IO多路复用

4.1.IO多路复用过程

4.2.IO多路复用监听方式

4.3.IO多路复用-select

4.4.IO多路复用-poll

4.5.IO多路复用-epoll

4.6.select poll epoll总结

4.7.IO多路复用-事件通知机制

4.8.IO多路复用-web服务流程

5.信号驱动IO 

6.异步IO

7.同步和异步


前言

Redis 以其卓越的性能和灵活的特性,成为众多开发者在缓存、消息队列等场景的首选。而 Redis 强大性能的背后,其网络模型与 IO 机制发挥着关键作用数据的读写速度直接影响着用户体验。大家日常逛的电商平台,流畅加载商品详情页离不开它;刷社交软件时,点赞瞬间显示也有它的功劳 。接下来,我们就深入挖掘一下,看看 Redis 是如何通过它们实现高效运转的。

 

1.用户空间和内核空间

为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的:

进程的寻址空间会划分两部分:内核空间,用户空间

这就代表了一个完整的32位寻址空间

那什么是寻址空间呢?

寻址空间是指计算机系统中处理器能够访问的内存地址范围。

计算机中的内存是由许多存储单元组成的,每个存储单元都有一个唯一的编号,这个编号就是地址。处理器通过地址来访问内存中的数据和指令。寻址空间就是这些地址的集合,它决定了处理器能够访问的内存大小。

我们还要在系统权限上进行划分,因为我们cpu运行的各种各样的命令里边,有一些命令风险等级比较低,有一些比较高。所以cpu会把各种各样的命令划分成四个不同的等级,Ring0风险等级最低,Ring3风险等级最高

  • 用户空间只能执行受限的命令(Ring3),而且不能直接调用系统资源,必须通过内核提供的接口来访问
  • 内核空间可以执行特权命令(Ring0),调用一切系统资源

 

1.2用户空间和内核空间的切换

应用程序在用户空间,内核应用在内核空间,但是我们一个进程它在执行过程中因为业务比较多,可能会执行一些普通命令和特权命令调用系统资源。因此进程会在用户空间和内核空间之间进行一个转换

linux系统为了提高io效率,会在用户空间和内核空间都加入缓冲区

  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备
  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区

 

1.3切换过程

IO在用户空间和内核空间切换流程

写数据到磁盘过程

  • 1.进程在做一些简单运算,字符串处理,之后要把数据写出到我们的磁盘需要调用我们的内核
  • 2.写数据先写到缓冲区,要往磁盘写必须要切换到内核
  • 3.切换到内核,内核没有我要写的数据,所以要先把用户缓冲区的数据拷贝到内核的缓冲区,然后再把缓冲区的数据写入我们的磁盘

读数据到用户空间

  • 1.开始用户空间发起read的请求,请求到达内核空间判断有没有数据,如果要读的是磁盘,要先去寻址wati,for data,寻址到之后把数据读取到缓冲区
  • 2.把数据从内核缓冲区拷贝到用户空间用户再区处理这些数据

从上图可知IO读写效率的主要原因是

  • 1.数据等待过程,(用户空间读数据发起read请求,内核空间接收到这个请求需要去寻址和把数据写到缓冲区)
  • 2.就是数据拷贝过程非常影响性能,一个空间缓冲区数据拷贝到另一个空间缓冲区

数据拷贝是由操作系统内核来完成的。

 

2.阻塞IO

不同IO模型的差别就是在1.等待数据就绪 2.读取数据过程

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

所以阻塞IO就是2.把数据写到内核缓冲区1.把内核缓冲区数据写到用户空间缓冲区这两个阶段都是阻塞状态。

 

3.非阻塞IO

顾名思义,非阻塞IO的recvfrom操作会立即返回结果而不是阻塞用户进程。

非阻塞IO就是第一阶段不停调用recvfrom去读取数据读取不到不会阻塞一直反复调用直到成功(反而让cpu使用率增加)

然后第二阶段内核拷贝数据到用户空间依然是阻塞状态

4.IO多路复用

无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom来获取数据,差别在于无数据时的处理方案:

  • 如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。
  • 如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据

比如服务端处理客户端Socket时,在单线程情况下,只能依次处理每一个socket,如果正在处理的socket恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有其它客户端socket都必须等待,性能自然会很差。

解决方案就是数据就绪了,用户应用就去读取数据

用户进程如何知道内核中数据是否就绪呢?

4.1.IO多路复用过程

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


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

IO多路复用过程:

  1. 用户应用首先去调用select函数,不在是调用recvfrom(recvfrom直接是尝试读取数据读的目标是具体某一个FD)
  2. select函数内部可以接收多个FD,也就是说可以把每个客户端socket对应的FD,传给select函数,然后传入到内核中,内核就可以去检查你要去监听的多个FD,有没有任何一个是就绪的,只要由任意一个或者多个就绪就会直接返回这个结果。
  3. 如果这n个FD都没有就绪那么就会稍微等一会,在等待过程中其实就会有后台进程去监听这些FD,一旦有任意一个或者多个就绪返回结果。这个等待不可避免的。
  4. 拿到readable结果了就去调用recvfrom我们可以明确知道哪些FD准备好啦,然后拷贝数据返回结果(循环调用)

其实IO多路复用1.等待数据就绪用户进程也是阻塞阶段,阶段二数据拷贝同样是阻塞的

区别在于:

  • 阻塞IO调用的是调用recvfrom去监听某一个FD有没有就绪,没有就会阻塞
  • IO多路复用调用的是select函数去监听多个FD,只要有一个FD就绪就去处理

4.2.IO多路复用监听方式

IO多路复用监听FD的方式,通知的方式又有多种实现,常见的有:

select

poll

epoll

差异:

  • select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
  • epoll则会在通知用户进程FD就绪的同时,把已就绪的FD写入用户空间

 

4.3.IO多路复用-select

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

select函数:

1.nfds:这是需要监视的最大文件描述符加 1。举例来说,若要监视的文件描述符为 3、4、5,那么nfds的值就是5 + 1 = 6

为了能更精细地管理和监控不同类型的 IO 事件,select将FD分成三个集合

2.readfds该集合用于监视文件描述符的可读事件。

3.writefds:此集合用于监视文件描述符的可写事件。

4.exceptfds:这个集合用于监视文件描述符的异常事件。

执行流程:

首先用户空间

  • 1.创建fd_set rfds集合
  • 2.fd_set集合底层使用fds_bits[]来监听,共可以监听1024个Fd,要监听哪个就把哪个位置变为1。比如要监听fd = 1,2,5
  • 3.执行select函数,把fds_bits[]数组拷贝到内核空间

内核空间

  • 1.首先遍历fd_set看有没就绪
  • 2.没有就绪则睡眠,后台监听
  • 3.等待数据就绪被唤醒或超时
  • 4.fd = 1数据就绪,其他没有就绪剔除,返回结果有几个就绪了,拷贝到用户空间遍历哪个就绪了

处理完之后再次把要监听数据放到集合里执行select去监听往复处理数据

缺点:

  • 1.需要将整个fd_set从用户空间拷贝到内核空间,select结束还要再次拷贝回用户空间
  • 2.select无法得知具体哪个FD就绪,需要遍历整个FD_set,
  • 3.fd_set监听的fd数量不能超过1024

 

4.4.IO多路复用-poll

poll模式对select模式做了简单改进,但性能提升不明显,部分关键代码如下:

poll参数部分和select差不多主要区别在于fds,pollfd数组,没有去划分不同事件集合全部划分到一个数组当中。区别是哪种事件主要在于结构体中 events属性不同值代表不同监听类型

revents表示实际发生的事件类型,内核会把就绪的事件类型放在这个集合里,超时时间过了还没有就绪FD,就把这个值给成0返回给用户空间,这样一来就知道这个FD有没有发生事件。

IO流程:

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越多,每次遍历消耗时间也越久,性能反而会下降

4.5.IO多路复用-epoll

epoll模式是对select和poll的改进,它提供了三个函数:

eventpoll结构体:使用红黑树存储要监听的FD,和使用链表记录就绪的FD

2.接下来我们就要去监听FD了,会使用到第二个函数

这个函数epoll_ctl将我们一个FD添加到eventpoll里面,相当于监听FD

传入的参数包括(需要添加到哪个eventpoll,是增删改哪个操作,要监听的fd,监听事件类型

3.第三个函数就是等待FD的监听就绪。函数传入参数(eventpoll指针,空数组用于接收就绪的FD,events数组最大长度,超时时间)返回就绪的数量

那么我怎么知道哪个FD就绪了呢?空数组就派上用场了

我们把这个数组拷贝到用户空间就知道哪些FD就绪了,不用去遍历

4.6.select poll epoll总结

select模式存在的三大问题:

  • 能监听的FD最大不超过1024
  • 每次select都需要把所有要监听的FD都拷贝到内核空间
  • 每次都要遍历所有FD来判断就绪状态

poll模式的问题:

poll模式利用链表解决了select中监听FD上限问题,但依然要遍历所有FD,如果监听较多,性能会下降

epoll模式中如何解决这些问题的?

  • 基于epoll实例中的红黑树保存要监听的FD,理论上无上限,而且增删改查效率都非常高,性能不会随监听FD数量增多而下降
  • 每个FD只需要执行一次epoll_ctl添加到红黑树,以后每次epol_wait无需传递任何参数,无需重复拷贝到Fd到内核空间
  • 内核会将就绪的FD拷贝到用户空间的知道位置,用户进程无需遍历所有FD就知道就绪的FD是谁

 

4.7.IO多路复用-事件通知机制

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

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

举个栗子:

  1. 假设一个客户端socket对应的fd已经注册到了epoll实例中
  2. 客户端socket发送了2kb的数据
  3. 服务端调用epoll_wait,得到通知说fd就绪
  4. 服务端从fd读取了1kb数据
  5. 回到步骤3(再次调用epoll_wait,形成循环)

总结:

ET模式避免了LT模式可能出现惊群现象

ET模式最好结合非阻塞IO读取FD数据,相比LT会复杂一些

4.8.IO多路复用-web服务流程

serverSocket:接收客户端请求

 

5.信号驱动IO 

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

首先信号驱动IO第一阶段处理流程:

  1. 用户进程一上来不是调用recvfrom,而是系统调用sigaction指定FD绑定信号处理函数,立即结束不用阻塞等待,
  2. 如果没有数据内核帮我们监听如果有数据了帮我们去唤醒并且提交一个信号给我们用户进程,
  3. 之前建立的SIGIO信号处理函数就去处理这个信号,整个过程用户不用去等待可以去干其他事情

第二阶段和其他阻塞IO一致

那么你可能会问这个IO这么好为什么不用而要去用多路复用IO?

当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出

而且内核空间与用户空间频繁的信号交互性能也较低。

 

6.异步IO

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

  • 1.异步IO并没有调用recvfrom函数而是调用aio_read通知内核说我想读哪个FD,读到哪里去就结束了。
  • 2.内核把数据准备就绪再把数据拷贝完成通知用户进程

异步IO在两个阶段都是非阻塞的

这种模式听着非常好但是高并发场景下如果对异步 I/O 的并发度控制不当,可能会导致系统资源过度使用,从而影响性能。

过多的异步网络请求可能会导致网络拥塞,降低系统的响应速度。

 

7.同步和异步

IO操作是同步还是异步,关键看数据在内核空间与用户空间的拷贝过程(数据读写的IO操作),也就是阶段二是同步还是异步:

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

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

相关文章

vue3 传参 传入变量名

背景: 需求是:在vue框架中,接口传参我们需要穿“变量名”,而不是字符串 通俗点说法是:在网络接口请求的时候,要传属性名 效果展示: vue2核心代码: this[_keyParam] vue3核心代码&…

旅游特种兵迪士尼大作战:DeepSeek高精准路径优化

DeepSeek大模型高性能核心技术与多模态融合开发 - 商品搜索 - 京东 随着假期的脚步日渐临近,环球影城等备受瞩目的主题游乐场,已然成为大人与孩子们心中不可或缺的节日狂欢圣地。然而,随之而来的庞大客流,却总让无数游客在欢乐的…

【MySQL】第一弹——MySQL数据库结构与操作

目录 一. 数据库介绍1.1 什么是数据库1.2 为什么要使用数据库1.3 主流数据库1.3.1 关系型数据库1.3.2 非关系型数据库 二. MySQL 的结构2.1 MySQL服务器和客户端2.2 MySQL服务器是如何组织数据的 三. 数据库的操作3.1 创建数据库语法格式示例 3.2 查看数据库语法格式示例 3.3 使…

Spring_MVC 快速入门指南

Spring_MVC 快速入门指南 一、Spring_MVC 简介 1. 什么是 Spring_MVC? Spring_MVC 是 Spring 框架的一个模块,用于构建 Web 应用程序。它基于 MVC(Model-View-Controller)设计模式,将应用程序分为模型(M…

L38.【LeetCode题解】四数之和(双指针思想) 从汇编角度分析报错原因

目录 1.题目 2.分析 去重的代码 错误代码 3.完整代码 提交结果 1.题目 四数之和 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元…

字符串系列一>最长回文子串

目录 题目:解析:代码: 题目: 链接: link 解析: 代码: class Solution {public String longestPalindrome(String s) {char[] ss s.toCharArray();int n ss.length;int begin 0;//返回结果的起始字符串…

双击热备方案及不同方案的需求、方案对比

双击热备方案概述 一般实现双机热备的方案有三种,分别是共享存储双机热备方案、镜像双机热备方案、双机双柜双机热备方案,这三种方案对硬件要求不同,大家可以根据自身的业务应用特性来选择具体的双机热备方案以及对应的ServHA双机热备软件产品。 1、镜像双机热备 1.1镜像…

VSCODE插值表达式失效问题

GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_-CSDN博客 更换正确的vue域名 GET https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js net::ERR_CONNECTION_ <script src"https://unpkg.com/vue2.6.14/dist/vue.js"></sc…

【失败】Gnome将默认终端设置为 Kitty

起因 一会儿gnome-terminal一会儿kitty终端&#xff0c;实在是受不了&#xff0c;决定取缔默认的gnome-terminal。 过程 在 Ubuntu 或 Debian 系统上&#xff1a; 确保 Kitty 已经安装。如果未安装&#xff0c;可以在终端中运行命令sudo apt install kitty -y进行安装。 使用系…

蓝桥杯:连连看

本题大意要我们在一个给定的nxm的矩形数组中找出符合条件的格子 条件如下&#xff1a; 1.数值相同 2.两个横坐标和纵坐标的差值相等&#xff08;由此可得是一个对角线上的格子&#xff09; 那么根据以上条件我们可以用HashMap来解决这个问题&#xff0c;统计对角线上数值相同…

Ext系列⽂件系统

Ext系列⽂件系统 1. 理解硬件1.1 磁盘的物理结构1.2 磁盘的存储结构1.3 磁盘的逻辑结构理解过程实际过程 1.4 CHS&&LBA地址 2. 引入文件系统块分区innode 3. Ext2文件系统3.1 宏观认识3.2 block group3.3 块组内部3.3.1 GDT&#xff08;Group Descriptor Table&#xf…

MTK-Android12 13 屏蔽掉Viewing full screen

去掉ROOM 开机第一次提示全屏弹框 文章目录 需求参考资料修改文件实现方案 解决思路grep 源码查找信息grep 查找 grep -rn "Viewing full screen" 找string 字段grep 查找 grep -rn immersive_cling_title 布局grep 查找 grep -rn layout.immersive_mode_cling 对应的…

GitHub创建远程仓库

使用GitHub创建远程仓库&#xff1a;从零开始实现代码托管与协作 前言 在当今软件开发领域&#xff0c;版本控制系统已成为开发者必备的核心工具。作为分布式版本控制系统的代表&#xff0c;Git凭借其强大的分支管理和高效的协作能力&#xff0c;已成为行业标准。而GitHub作为…

【AI部署】腾讯云GPU -—SadTalker的AI数字人访问web服务—未来之窗超算中心

访问部署在Cloud Studio上的web服务 当你把该项目部署在本地时&#xff0c;访问该服务的请求地址为http://localhost:8080/hello&#xff1b;当你把该项目部署在Cloud Studio工作台启动时&#xff0c;要想访问到该服务&#xff0c;需要先在工作台右侧打开访问链接面板&#xff…

fastdds:传输层SHM和DATA-SHARING的区别

下图是fastdds官方的图&#xff0c;清晰地展示了dds支持的传输层: 根据通信双方的相对位置(跨机器、同机器跨进程、同进程)的不同选择合适的传输层&#xff0c;是通信中间件必须要考虑的事情。 跨机器&#xff1a;udp、tcp 跨机器通信&#xff0c;只能通过网络&#xff0c; f…

树莓派_利用Ubuntu搭建gitlab

树莓派_利用Ubuntu搭建gitlab 一、给树莓派3A搭建基本系统 1、下载系统镜像 https://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ 2、准备系统SD卡 二、给树莓派设备联网 1、串口后台登录 使用串口登录后台是最便捷的&#xff0c;因为前期网络可能不好直接成功 默…

ARINC818协议(三)

源特定参数 源特定参数被定义&#xff0c;用于在源和目的之间进行传输 源特定参数包括初始化&#xff0c;合适的解释&#xff0c;周期性的验证。 gamma or palette tables&#xff1a;伽马或者调色板 color format:颜色格式 Brightness and backlight control &#xff1a;亮度…

得佳胜哲讯科技 SAP项目启动会:胶带智造新起点 数字转型新征程

在全球制造业加速向数字化、智能化转型的浪潮中&#xff0c;胶带制造行业正迎来以“自动化生产、数据化运营、智能化决策”为核心的新变革。工业互联网、大数据分析与智能装备的深度融合&#xff0c;正推动胶带制造从传统生产模式向“柔性化生产精准质量控制全链路追溯”的智慧…

万字解析TCP

通过学习视频加博客的组合形式&#xff0c;整理了一些关于TCP协议的知识。 *图源&#xff1a;临界~的csdn博客。 一、TCP建立连接 TCP的建立连接&#xff0c;大致可以分为面向连接、TCP报文结构、TCP的三次握手、TCP的建立状态、SYN泛洪攻击。 1.1、面向连接 面向连接 --- …

2025年大数据实训室建设及大数据实训平台解决方案

一、引言 在数字化浪潮中&#xff0c;大数据技术已成为推动各行业创新发展的核心驱动力。从金融领域的风险预测到医疗行业的精准诊断&#xff0c;从电商平台的个性化推荐到交通系统的智能调度&#xff0c;大数据的应用无处不在。据权威机构预测&#xff0c;到 2025 年&#xf…