文中的网络线程:执行网络io所在的线程
阻塞、非阻塞
- 阻塞:典型的一次IO一般分为两个阶段:数据准备以及数据拷贝(数据在内核空间与用户空间之间的往返)。在数据准备阶段,调用IO方法的线程进入阻塞状态。例如recv方法默认就是阻塞的,如果没有数据到来就会阻塞当前线程,当前线程将会挂起,状态从运行态变为阻塞态
- 非阻塞:典型的一次IO一般分为两个阶段:数据准备以及数据拷贝。在数据准备阶段,即使没有数据,调用IO方法的线程也不会被阻塞,而是直接返回值。例如:当套接字设置为非阻塞的,调用recv方法将会立即返回,即使内核的接收缓冲区没有数据。这样的话线程还能有机会继续运行着。但是在非阻塞状态下,用户线程需要不断调用IO方法询问内核是否准备好数据。
- 综上:根据系统io操作的就绪状态,网络线程的表现来判断一个io方法是阻塞的还是非阻塞的。
同步、异步
- 同步:典型的一次IO一般分为两个阶段:数据准备以及数据拷贝。在数据拷贝阶段,例如recv接口就是一个同步IO接口,如果TCP内核的接收缓冲区有数据,用户态的应用程序则会等将内核的接收缓冲区数据拷贝到用户缓冲区以后才会继续执行下去。一些IO复用函数例如poll、epoll、select都是同步IO
- 异步:典型的一次IO一般分为两个阶段:数据准备以及数据拷贝。在数据拷贝阶段,用户态的应用程序需要给内核传递一个buffer(这是一个传入传出参数)以及指定一种通知方式,当内核准备好数据就会以信号、事件回调等方式通知应用程序。这一期间,用户态的应用程序可以继续执行下去,不用等内核准备数据。例如aio_read接口以及aio_write接口。
- 综上:根据应用程序和内核的交互方式来判断异步和同步,在数据读写阶段,应用程序是否需要等待数据的准备才可以继续执行其他逻辑。
五种网络IO模型
- 阻塞IO:分为同步阻塞IO和异步阻塞IO。同步阻塞IO常用,异步阻塞IO一般少见。在linux中默认创建的套接字都是阻塞的。
- 非阻塞IO:分为同步非阻塞IO和异步非阻塞IO。对于同步非阻塞IO,因为非阻塞IO会立即返回,所以一般需要在循环中去判断数据是否准备好,但这个循环判断也是在白白消耗CPU,没有意义。因此只有在事件已经发生的情况下操作非阻塞IO才能提高程序的效率,因此开发中常使用IO多路复用或者信号驱动。非阻塞io模型与阻塞io模型的相同点是在数据的拷贝阶段,网络线程都是阻塞的。
- IO多路复用:可以同时处理多个网络连接的IO。IO复用函数作用于数据准备阶段,在这个阶段中默认是阻塞的,但是可以通过超时参数设置成非阻塞的。
- 信号驱动:应用程序调用
系统调用
来注册信号的处理程序,当发生相应的事件以后,内核会通知应用程序,在通知以前应用程序可以继续执行其他逻辑。 - 异步IO:数据从内核空间拷贝到用户空间或者从用户空间拷贝到内核空间,也由操作系统完成,而不是由用户程序完成。用户程序可以放飞自我,继续执行其他的逻辑。数据由操作系统拷贝完成以后,应用程序处理数据。异步IO与非阻塞IO的区别就是使用非阻塞IO,需要用户线程自己将数据从内核态拷贝到用户态,这段时间用户线程还是阻塞的。
Reactor
1.简介
- Reactor是一种高效的事件处理模式,通常用于实现非阻塞同步IO模型,这里的非阻塞是指套接字设置为非阻塞的。
- 在Reactor模式中,主线程只负责监听文件描述符上是否有事件发生,有的话立即将事件通知工作线程。读写数据、接受新的连接、以及处理客户请求都在工作线程中完成。
- 使用Reactor事件处理模式的实例:libevent、muduo、redis
2.组件
- Reactor管理器:定义一些接口,用于上层注册、删除事件处理器。Reactor管理器内部会使用同步事件分发器来等待事件的产生,当相应的事件产生以后,会通过某种调度机制调用事件处理器来处理事件。
- 事件处理器:不同事件类型产生触发的回调实现
- 同步事件分发器:一个IO多路复用函数,用来等待一个或者多个事件的产生。
- 事件源:如IO读写、定时器、信号事件。
参考:<<深入浅出Libevent>>
3.实现
Reactor模式在编程实现上由非阻塞IO和IO多路复用两者组成,IO多路复用作用于数据准备阶段,非阻塞IO作用于数据拷贝阶段。
- 单Reactor模型:在一个Reactor中完成接收客户端的连接请求,与客户端的数据通信,如Redis6.0中采用的就是单Reactor模式。
- 单Reactor模型+任务队列+线程池:这种方式可以将网络IO和业务逻辑处理分离。比如说网络线程中将读取的数据存入任务队列中,多个工作线程处理任务队列。典型代表有skynet。
- 多Reactor
- 多Reactor+消息队列+线程池:一个线程负责侦听连接,其他线程处理读写事件。典型代表有muduo,memcached
- 多进程:每一个子进程都是一个Reactor,中完成接收客户端的连接请求,与客户端的数据通信。典型代表有nginx
Proactor
- Reactor是一种高效的事件处理模式,通常用于实现异步IO模型。
- Proactor模式与Reactor模式的区别是:
- Reactor模式:事件就绪后,由操作系统通知应用进程,让应用进程进行处理。
- Proactor模式:事件就绪后,由操作系统处理,处理完成后通知应用进程。
- Proactor模式与Reactor模式的共同点:基于事件分发
- 使用Proactor事件处理模式的实例:Windows上的IOCP