Redis网络模型

用户空间和内核空间

服务器大多都采用Linux系统,这里我们以Linux为例来讲解:
任何Linux发行版,其系统内核都是Linux.我们的应用都需要通过Linux内核与硬件交互。
在这里插入图片描述
为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的:

  • 进程的寻址空间会划分为两部分:内核空间和用户空间
  • 用户空间只能执行受限的命令(RIng3),而且不能直接调用系统资源,必须通过内核提供的接口来访问
  • 内核空间可以执行特权命令(Ring0),调用一切系统资源
    在这里插入图片描述
    在这里插入图片描述
    Linux系统为了提高IO效率,会在用户空间和内核空间都加入缓冲区:
  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备
  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区
    在这里插入图片描述
    可以看出在进行数据读写的时候,有两个地方是比较浪费时间的,一个是用户对数据的等待过程,一个是缓冲区进行数据拷贝的过程,所以为了提高效率,Linux提出不同的IO模型。

阻塞IO

在这里插入图片描述
阻塞IO,顾名思义就是两个阶段都必须等待:
在这里插入图片描述
可以看到,阻塞IO模型中,用户进程在两个阶段都是阻塞状态。

非阻塞IO

顾名思义,非阻塞IO的recvtrom操作会立即返回结果而不是阻塞用户进程。
在这里插入图片描述
可以看到,非阻塞IO模型中,用户进程在第一个阶段是非阻塞,第二个阶段是阻塞状态。虽然是非阻塞,但性能并没有得到提高。而是忙等待机制会导致CPU空转,CPU使用率暴增。

IO多路复用

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

  • 如果调用recvfrom时,恰好没有数据,阻塞IO会使进程阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。
  • 如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据。
    文件描述符(File Descriptor):简称FD,是一个从0开始递增的无符号整数,用来关联Linux中的一个文件。在Linux中一切皆文件,例如常规文件、视频、硬件设备等。当然也包括网络套接字(Socket)。
    **IO多路复用:**是利用单个线程来同时监听多个FD,并在某个FD可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。
    不过监听FD的方式,通知的方式又有多种实现,常见的有:
  • select
  • poll
  • epoll
    差异:
  • select和poll只会通知用户进程有FD就绪,但不确定具体是哪个FD,需要用户进程逐个遍历FD来确认
  • epoll则会通知用户进程FD就绪的同时,把已经就绪的FD写入用户空间

在这里插入图片描述

IO多路复用 - select

select 是Linux中最早的IO多路复用实现方案:
在这里插入图片描述
在这里插入图片描述
select模式存在的问题:

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

IO多路复用 - poll
poll模式对select模式做了简单改进,但性能提升不明显,部分关键代码如下:
在这里插入图片描述
IO流程:
IO多路复用 - epoll
epoll模式是对select和poll的改进,他提供了三个函数:
在这里插入图片描述
在这里插入图片描述
总结:
在这里插入图片描述
IO多路复用-web服务流程
基于epoll模式的web服务的基本流程如图:
在这里插入图片描述

信号驱动IO

在这里插入图片描述

异步IO

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

Redis网络模型

在这里插入图片描述
在这里插入图片描述
Redis通过IO多路复用来提高网络性能,并且支持各种不同的多路复用实现,并且将这些实现进行封装,提供了统一的高性能事件库API库AE:
在这里插入图片描述
来看下Redis单线程网络模型的整个流程:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最终流程
在这里插入图片描述
以上流程是Redis6.0以前单线程的IO多路复用的流程,也就是使用的IO多路复用+监听事件派发的机制,首先进行的是对server socket的初始化过程,构建红黑树以及一个监听事件就绪的链表,然后通过三个不同的处理器对所监听到的事件进行不同的处理,如果监听到的是server socket有可读的ssfd,那么就把它注册到需要进行监听的红黑树中;如果监听的是client socket的readable事件,那么就接受客户端的请求,把请求参数注册到缓冲区里,进行解析读取并且处理,最后将结果写入输出缓冲区中,等待结果排队输出;然后会触发client socket的writeable事件将结果写回客户端。

Redis6.0版本中引入了多线程,目的是为了提高IO读写效率。因此在解析客户端命令、写响应结果时采用了对线程。核心命令的执行、IO多路复用模块依然是由主线程执行。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/xiaowanziddd/article/details/125561554