阻塞与非阻塞IO
- 阻塞是阻塞在 内核态向 网卡buffer 读写数据的时候。阻塞IO会等待数据,非阻塞IO 则是没有数据立即返回
- 通过创建 fd 的时候决定是否阻塞
IO多路复用
- BIO (blocking I/O) 的方式是 主线程一直在等待连接处理
- IO 多路复用,复用的是网络线程,该网络线程可以实现对多个读写 fd 的处理,利用非阻塞IO,在内核阻塞等待的时候,可以让出 CPU 去处理其他事件
- IO 多路复用,是基于循环 + 事件驱动、事件回调的机制,缺点是,容易分离业务逻辑
- Linux 中有 3 种多路复用模型:
- select: 基于 fd 的状态,如果发生可读可写或者 except,需要遍历 fd 列表进行获取就绪的 fd,缺点是单个进程能够打开的 fd 数量有限,二是线性扫描过于浪费时间,并且需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
- poll:
- epoll: 使用一个文件描述符管理多个描述符,将用户关系的文件描述符和事件存放到内核的一个事件表中( 红黑树 ),这样在用户空间和内核空间的copy只需一次,在有事件触发之后,将会放到内核态中的一个事件列表中进行返回
为什么不使用多进程加多线程
- 多进程依赖于 linux 中的 fork 机制,而 fork 机制只能复制当前 thread 的上下文信息,不克隆其他
- 多线程编程中会有很多 系统调用、加锁、malloc 等内容,fork 难以决定是否复制
为什么 nginx 使用 Reactor + 多进程模型
- http 连接本身是无状态的,利用多进程可以较好的实现运行环境隔离性,可以通过 slab 共享内存 实现简单高效的数据统一
- 多个 worker 进程都会进行 epoll 的 listern,通过 共享内存 + 进程锁 的方式,决定一个连接只会被一个 worker 所处理