IO多路复用

I/O多路复用的意思就是一个进程同时处理多个TCP连接。

  • I/O:一般指网络I/O。
  • 多路:多个客户端连接(连接就是套接字描述符,即socket或channel),指多个TCP连接。
  • 复用:用一个进程来处理多条连接,使用单进程就能够实现同时处理多个客户端的连接。

在多路复用之前使用的是同步阻塞I/O模型,因为一个请求会阻塞进程/线程,所以要为每个请求分配一个进程/线程,一个进程/线程处理一个网络连接,进程/线程的上下文切换消耗很大,并且创建进程/线程也会有一定消耗,所以同步阻塞I/O模型性能很低。

Linux有三种实现I/O多路复用的机制:select -> poll -> epoll。

开始了解多路复用之前,先要了解操作系统的以下几个概念:

  1. 文件句柄

    Linux中一切皆文件,所有内容都是以文件的形式来保存和管理的。文件句柄(File Handle)是操作系统中用于访问文件的一种数据结构,通常是一个整数或指针。文件句柄用于标识打开的文件,每个打开的文件都有一个唯一的文件句柄。

  2. 用户态和内核态

    • 用户态(User Mode),运行用户程序。
    • 内核态(Kernel Mode),运行操作系统程序,操作硬件。

    在内核态下代码可以执行任何CPU指令以及引用任何内存地址,用户程序不能直接进入内核态,需要通过系统调用。

  3. 同步和异步

    同步和异步是指访问数据的机制,同步一般指主动请求并等待I/O操作完成的方式。异步指主动请求数据后便可以继续处理其他任务,随后等待I/O操作完毕的通知。

  4. 阻塞和非阻塞

    阻塞指的是进程调用接口后如果接口没有准备好数据,那么这个进程会被挂起什么也不能做,直到有数据返回时唤醒。非阻塞就是进程调用接口后如果接口没有准备好数据,进程也能处理后续的操作,但是需要不断地去轮询检查数据是否已经处理完成。

  5. 并行和并发

    并行指的是多个事情在同一个时间点上同时发生了。并发指的是多个事情,在同一时间段内同时发生了。

select

接收到多个请求后,将对应的文件描述符数据存储到一个数组中,并用一个bitmap类型的rset数据存储需要被监听的文件描述符。

将rset整个复制到内核态中,在内核态中判断是否有数据到来,如果没有数据来,内核态会一直判断,此时用户程序是阻塞状态。

当有数据的时候,内核会将有数据的fd置位,表示有数据来了,然后select函数会返回,程序会继续执行,将有数据的文件标志符中的数据读出来,并进行相应的处理。

缺点:

  1. bitmap的大小是1024,即使这个值是可以设置的,也存在上限。
  2. fdset不可重用,每次都需要重新设置一下,因为rset的值在内核中被修改了。
  3. 用户态和内核态的切换仍然有一定的开销。
  4. rset被置位之后,仍然不知道是哪些被置位了,需要再去遍历一遍,时间复杂度为 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)。

poll

poll和select的方式一样,但是使用了pollfd,有数据的时候是pollfd.revents置位,(而不是像select一样修改整个rset),然后poll返回。

因为没有使用bitmap,所以没有文件描述符数量的限制,因为没有使用rset,直接修改了文件描述符数组中的某一项元素的revents,所以文件描述符数组是可以复用的。

poll解决了select存在的1、2两个问题,但是3、4两个问题依然没有解决。

epoll

epoll中用户态和内存态共享epfd的内存。不需要用户态和内核态的拷贝了。

有数据的时候,通过重排来置位,将有数据的fd放到最前面的位置,然后返回。epoll_wait是有返回值的,如果有3个fd触发了事件,就会返回3,之后就只需要遍历数组的前3个元素,对数组的前3个元素进行数据的读取和处理就行了,操作的时间复杂度是 <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( 1 ) O(1) </math>O(1)。

epoll解决了select存在的问题1、2、3、4。

Redis和Nginx,以及Java的NIO底层就是用epoll来实现的。

Redis与IO多路复用

从Redis6开始,将网络数据读写,请求协议解析通过多个IO线程来处理。对于真正的命令执行,依然使用单线程操作。Redis中的命令执行都是单线程的,性能比较好的原因除了是基于内存操作外,还有一个更主要的原因就是使用了IO多路复用。

Redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,一次放到文件事件派发器,事件派发器将事件分发给事件处理器。

学习地址

RedisIO多路复用:www.bilibili.com/video/BV13R...

IO多路复用:www.bilibili.com/video/BV1qJ...

相关推荐
morris1315 小时前
【redis】redis实现分布式锁
数据库·redis·缓存·分布式锁
爱的叹息7 小时前
spring boot集成reids的 RedisTemplate 序列化器详细对比(官方及非官方)
redis
weitinting8 小时前
Ali linux 通过yum安装redis
linux·redis
纪元A梦9 小时前
Redis最佳实践——首页推荐与商品列表缓存详解
数据库·redis·缓存
爱的叹息16 小时前
Java 连接 Redis 的驱动(Jedis、Lettuce、Redisson、Spring Data Redis)分类及对比
java·redis·spring
松韬17 小时前
Spring + Redisson:从 0 到 1 搭建高可用分布式缓存系统
java·redis·分布式·spring·缓存
天上掉下来个程小白17 小时前
Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
java·redis·spring·springboot·苍穹外卖
·云扬·18 小时前
深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战
redis·mysql·缓存
汤姆大聪明18 小时前
Redisson 操作 Redis Stream 消息队列详解及实战案例
redis·spring·缓存·maven
csjane10791 天前
Redis原理:rename命令
java·redis