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被置位之后,仍然不知道是哪些被置位了,需要再去遍历一遍,时间复杂度为 O ( n ) O(n) 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个元素进行数据的读取和处理就行了,操作的时间复杂度是 O ( 1 ) O(1) 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...

相关推荐
yurenpai(27届找实习中)9 小时前
redis_点评(21.好友关注——关注、取关功能实现;共同关注功能实现)
数据库·redis·缓存
z200509309 小时前
【linux学习】深入理解linux文件I/O,从C标准库到内核态
linux·学习·操作系统
Trouvaille ~10 小时前
【Redis篇】Set 与 Zset:集合运算与排行榜的终极武器
数据库·redis·缓存·set·跳表·后端开发·zset
量子炒饭大师11 小时前
【Linux系统编程:进程概念】——【从 冯诺依曼系统体系结构 到 操作系统】
linux·运维·服务器·操作系统·冯诺依曼
带娃的IT创业者12 小时前
数字考古学:当整个操作系统史被装进一个浏览器
操作系统·前端开发·webassembly·虚拟化技术·数字考古学·windows 95·复古计算
小小工匠13 小时前
Redis - 基本架构:一个键值数据库到底由什么组成
数据库·redis·架构
步十人15 小时前
【Redis】网络高并发模型
网络·数据库·redis
我是一颗柠檬15 小时前
【Redis】列表与集合Day4(2026年)
数据库·redis·后端·缓存
Devin~Y15 小时前
从内容社区到AIGC客服:Spring Boot、Redis、Kafka、K8s、RAG的三轮大厂Java面试对话(附标准答案)
java·spring boot·redis·spring cloud·kafka·kubernetes·micrometer
Xzh042315 小时前
Redis黑马点评 实战复盘与面试高频考点详解
java·数据库·redis·面试