并发方案
- PPC
- TPC
- preforking: 多个线程阻塞在同一个侦听端口上
accept
- Multiplexing
"惊群"问题
同步和异步
同步和异步是针对应用程序和内核的交互而言的
- 同步指的是用户进程触发I/O操作并等待或者轮询的去查看I/O操作是否就绪
- 异步是指用户进程触发I/O操作以后便开始做自己的事情, 而当I/O操作已经完成的时候会得到I/O完成的通知.
阻塞和非阻塞
阻塞和非阻塞是针对于进程在访问数据的时候, 根据I/O操作的就绪状态来采取的不同方式, 是一种读取或者写入操作函数的实现方式
- 阻塞方式下读取或者写入函数将一直等待
- 非阻塞方式下读取或者写入函数会立即返回一个状态值
I/O模型
同步阻塞I/O
用户进程在发起一个I/O操作以后, 必须等待I/O操作的完成, 只有当真正完成了I/O操作以后, 用户进程才能继续运行.
同步非阻塞I/O
用户进程发起一个I/O操作以后边可返回做其它事情, 但是用户进程需要时不时的询问I/O操作是否就绪, 这就要求用户进程不停的去询问(会引入不必要的CPU资源浪费).
异步阻塞I/O
应用发起一个I/O操作以后, 不等待内核I/O操作的完成. 内核完成I/O操作以后会通知应用程序(这同步和异步最关键的区别,同步必须等待或者主动的去询问I/O是否完成)
之所以称之为阻塞, 是因为通过select
等系统调用来完成的, 采用select
函数的好处是它可以同时监听多个文件句柄, 从而提高系统的并发性.
异步非阻塞I/O
用户进程只需要发起一个I/O操作然后立即返回, 等I/O操作真正的完成以后, 应用程序会得到I/O操作完成的通知. 此时用户进程只需要对数据进行处理就好了, 不需要进行实际的I/O读写操作, 因为真正的I/O读取或者写入操作已经由内核完成了.
Reactor和Proactor模式
Reactor和proactor都是IO多路复用模式
. I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)
.
分离器可将来自事件源的I/O事件分发到对应的read/write事件处理器(Event Handler)
.
应用程序需预先注册需要处理的事件及其事件处理器(或回调函数)
Reactor模式
Reactor模式应用于同步I/O的场景
Reactor设计模式中的要素如下:
Handles
: 网络连接, 文件句柄等, 是事件源Synchronous Event Demultiplexer
: 同步事件的派发, 如select
调用.(epoll/kqueue/iocp).Initiation Dispatcher
: 注册,移除和分派事件处理器Event Handler
: 事件处理器
读取操作具体步骤:
- 应用程序注册
读取事件
和相关联的事件处理器
- 事件分离器等待事件的发生(Reactor负责)
- 当发生读取事件的时候, 事件分离器调用第一步注册的事件处理器(Reactor负责)
- 事件处理器首先执行实际的读取操作, 然后根据读取到的内容进行进一步的处理(用户处理器负责)
Proactor模式
Proactor模式应用于异步I/O场景
读取操作具体步骤:
- 应用程序初始化一个异步读取操作, 然后注册相应的事件处理器. 此时事件处理器 不关注读取就绪事件, 而是 关注读取完成事件(区别于Reactor的关键)
- 事件分离器等待
读取操作完成
事件 - 在事件分离器等待读取操作完成的时候, 操作系统调用内核线程完成读取操作, 并将读取的内容放入 用户传递过来的缓存区中
- 事件分离器捕获到读取完成事件后, 调用应用程序注册的事件处理器. 事件处理器直接从缓存区读取数据, 而不需要进行实际的读取操作
对比可以看出, Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的: Reactor中需要应用程序自己读取或者写入数据; Proactor模式应用程序不需要进行实际的读写过程, 它只需要从缓存区读取或者写入即可, 操作系统会读取缓存区或者写入缓存区到真正的I/O设备.