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...

相关推荐
BergerLee4 小时前
对不经常变动的数据集合添加Redis缓存
数据库·redis·缓存
huapiaoy5 小时前
Redis中数据类型的使用(hash和list)
redis·算法·哈希算法
【D'accumulation】6 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
Cikiss6 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
一休哥助手7 小时前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
盒马盒马8 小时前
Redis:zset类型
数据库·redis
醉颜凉8 小时前
银河麒麟桌面操作系统V10 SP1:取消安装应用的安全授权认证
运维·安全·操作系统·国产化·麒麟·kylin os·安全授权认证
Jay_fearless9 小时前
Redis SpringBoot项目学习
spring boot·redis
Wang's Blog10 小时前
Redis: 集群环境搭建,集群状态检查,分析主从日志,查看集群信息
数据库·redis
wclass-zhengge15 小时前
Redis篇(最佳实践)(持续更新迭代)
redis·缓存·bootstrap