BIO、NIO、IO多路复用
- BIO(Blocking IO)
- NIO(Non-blocking IO) 同步非阻塞IO
- IO多路复用
- select
- poll
- epoll
- Redis的IO多路复用
BIO(Blocking IO)
最基础的IO模型,当进行IO操作时,线程会被阻塞,直到操作完成。
比如read和write,通常IO操作都是阻塞I/O的,也就是说当你调用read时,如果没有数据收到,那么线程或者进程就会被挂起,直到收到数据。无法处理并发。
阻塞 I/O 是最简单的 I/O 模型,一般表现为进程或线程等待某个条件,如果条件不满足,则一直等下去。条件满足,则进行下一步操作。
NIO(Non-blocking IO) 同步非阻塞IO
- 在这种模型中,线程可以在不阻塞的情况下对文件描述符进行读写操作。
- 当你调用read时,如果有数据收到,就返回数据,如果没有数据收到,就立刻返回一个错误(标识符),这样是不会阻塞线程了。此时用户进程需要不断轮询,如果轮询频繁,则浪费了大量的CPU资源;如果轮询频率低,则不能实时地获取数据。
不断轮询,过度浪费CPU资源。
IO多路复用
- 文件描述符:简称fd,是一个从0开始递增的无符号整数,用来关联Linux中的文件,在Linux中,一切皆文件,例如常规文件、视频,也包括网络套接字
- IO多路复用:利用单个线程可以监视并同时等待多个文件描述符,而不是使用多个线程(每个文件描述符一个线程,每次new一个线程),当这些文件描述符(套接字描述符)其中一个进入读就绪或者写就绪,select、poll、epoll就可以返回,从而避免无效的等待,充分利用CPU资源
select
可以看到select监听的文件描述符包括三个readfds、writefds、exceptfds,将用户传入的数组拷贝到内核空间,select会被阻塞,直到描述符就绪,返回。
selcet核心执行流程
- select是一个阻塞函数,当没有数据时,会一直阻塞在select。
- 当有数据时,会将对应的rset设置为1
- select函数返回,不在阻塞。遍历文件描述符判断那个fd置位,读取数据,然后处理。
缺点:
- bitmap 最大1024 一个进程最多处理1024个客户端。
- rset位不可重复用,每次socket有数据就会相应的位被置位。
- 文件描述符数组拷贝到了内核态,select调用需要传入fd数组,需要拷贝一份到内核,高并发场景下消耗的资源是惊人的。
- select没有通知用户态哪一个socket有数据,select仅返回可读文件的个数,具体哪个可读还需要用户自己O(N)遍历。
小结:select方式,既做到了一个线程处理多个客户端连接(文件描述符),又减少了系统调用的开销(多个文件描述符只有一次 select 的系统调用 + N次就绪状态的文件描述符的 read 系统调用
poll
在select类似,在select上做了一部分改进,并没有解决根本问题。
优点
- 使用数组来解决select的bitmap 1024限制。
- 有事件发生时,将对应的revents置位位为1,遍历的时候将对应的位置设置为0,可以实现重用。
缺点
本质上还是select方法
- poll 将fds数组拷贝到内核态,仍然有开销。
- poll并没有通知用户态那个socket有数据,还是需要O(N)遍历
epoll
- 会在通知用户进行FD就绪的同时。把已就绪FD写入用户空间,
- 无需遍历所有FD 多路复用快一次系统调用+内核层遍历这些文件描述符
- socket生命周期只会从用户态到内核态拷贝一次
- 使用event通知级值,每次socket有数据会主动通知内核加入就绪列表,不需要遍历所有socket
Redis的IO多路复用
redis中IO阻塞的问题
Rdis是跑在单线程中的,所有的操作都是按照顺序线性执行的,但是由于读写操作等待用户输入或输出都是阻塞的,所以I/O操作在一般情况下往往不能直接返回,这会导致某一文件的I/O阻塞导致整个进程无法对其它客户提供服务,而I/O多路复用就是为了解决这个问题而出现
Redis使用epoll实现IO多路复用
Redis利用epoll来实现IO多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
Redis服务采用Reactor的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)
Reactor模式,是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式。服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程
Reactor模式
Reactor模式也叫Dispatcher模式。即I/O多了复用统一监听事件,收到事件后分发(Dispatch给某进程),是编写高性能网络服务器的必备技术。Reactor模式中有2个关键组成:
- Reactor,Reactor在一个单独的线程中运行,负贵监听和分发事件,分发给适当的处理程序来对IO事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人
- Handlers,处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际办理人。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作