情景复现
面试官:Redis为什么这么快?
我:1. 基于内存 2. 高效数据结构 3. 单线程 4. IO多路复用
面试官:那你讲讲Redis的IO多路复用模型是什么。
我:哦,嗯,啊,呀...IO多路复用、文件描述符、用户态,内核态、哦。
Redis的IO多路复用模型是什么
I/O多路复用模型是什么?就是很多网络连接(多路),共(复)用少数几个(甚至是一个)线程。(来源:I/O多路复用技术(multiplexing)是什么?)
Redis采用I/O多路复用机制,使得Redis在单线程模式下依然可以高效的处理多个I/O流。
其核心思想是,先通过 select
/ poll
/ epoll
等系统调用查询监听的文件描述符是否准备就绪,这个操作可阻塞也可立即返回(具体看参数和对应规则),当其中一个或者多个文件描述符IO事件准备就绪才开始下一步,即 read
或者 write
等系统调用,这里才是真正的读或者写。(来源:redis 的 IO 多路复用如何?)
首先Redis IO多路复用采用 epoll
的实现方案,但是在了解 epoll
前先了解一下 select
与 poll
,对后面理解更有帮助。
select
- 一个客户端与服务端连接时,会生成对应一个套接字描述符(fd)
- 进程会将fd加入进程维护的fd列表中,每次调用
select
都需要将 fd列表从用户态进程拷贝到内核,(当fd列表较大时,拷贝开销不可忽略,所以限制其大小为1024) - 内核轮询fd列表,当无fd就绪时,进程阻塞。
- 当网络数据到达内核缓冲区时,网卡发出中断信号通知CPU,CPU收到中断信号,执行对应中断程序
- 中断程序将内核缓冲数据拷贝到对应文件描述符的接收缓冲区
- socket接收数据完毕后,中断程序将进程重新添加至工作队列,并将进程从等待队列中移除
- 进程重新进入工作队列,从阻塞出继续执行。
poll
- 创建pollfd数组,向其中添加关注的fd信息,数组大小自定义
- 调用poll函数,将pollfd数组拷贝到内核空间,转链表存储,无上限
- 内核遍历fd,判断是否就绪
- 数据就绪或超时后,拷贝pollfd数组到用户空间,返回就绪fd数量n
- 用户进程判断n是否大于0
- 大于0则遍历pollfd数组,找到就绪的fd
epoll
(未完待续)