网络

【高性能网络编程】基础知识2 IO多路复用与Reactor 模式

2021-05-11  本文已影响0人  交藤

对比几种不同的 IO

  1. 阻塞IO

    应用进程被阻塞,直到数据从内核缓冲区复制到应用进程缓冲区中才返回。
    特点:阻塞进程,CPU 利用率高

  1. 非阻塞IO
    应用进程执行系统调用之后,内核返回一个错误码。应用进程可以继续执行,但是需要不断的轮训(增加了系统调用)来获知 I/O 是否完成。
    特点:不阻塞进程,轮训增加了 CPU 消耗

  2. IO复用

    单个进程可以等待多个套接字中的任何一个变为可读。这一过程会被阻塞,当某一个套接字可读时,把数据从内核复制到进程中。
    特点:阻塞进程,无轮训 CPU

  3. 异步 I/O

    应用进程执行阻塞的调用会立即返回,应用进程可以继续执行,不会被阻塞,内核会在所有操作完成之后向应用进程发送信号。
    特点:不阻塞进程,无轮训 CPU

同步IO 与 异步IO

同步与异步体现在 内核态到用户态的数据拷贝 是否阻塞等待
IO复用 非异步IO,只是允许等待的 fd事件 变多了

需要注意IO复用本质是 同步IO

IO多路复用 异步IO

Epoll 与 Select

fd :文件描述符

Select

  1. 描述符有限,最大值 1024
  2. 查询 fd 均遍历数组,O(n) 复杂度
  3. 轮训就绪状态的 fd 均需要传入所有 fd
  4. 每次轮训传入fd 都会触发用户态到内核态的拷贝

Epoll

  1. 描述符无限制
  2. fd 存放结构为红黑树,就绪 fd 事件触发回调插入就绪链表,读写 fd 事件的性能 O(logN),查询就绪 fd 事件性能 O(1) (直接读取就绪链表)
  3. 通过就绪链表存放就绪状态的 fd 事件,查询时直接返回就绪链表
  4. 红黑树与就绪链表直接存放内核态中,避免了用户态与内核态间的大量拷贝 (epoll_wait 拷贝少量就绪 fd 句柄)

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

// epoll_create 创建 epoll,设定 fd 数量(内核开辟存储空间)
// epoll_ctl 管理 fd 事件的监控(红黑树的插入/删除)
// epoll_wait 返回相应就绪的事件的 fd(内核态向用户态拷贝就绪链表内的就绪事件)
epoll

LT 与 ET

ET 下读写方式:
    只要可读,就一直读,直到读完或失败
    只要可写,就一直写,直到数据发送完或失败

【经典问题】
1. LT 下 ‘可写就绪’ 一直触发,怎么解决
    要写数据时,添加fd可写事件通过 epoll_crt 添加到epoll里,写完后移除。
2. TCP accept 
    接收到连接到达时, while(accept) 循环处理所有连接,避免连接丢失

Reactor

反应堆模式
也叫 Dispatcher 模式,即 I/O 多路复用监听事件,收到事件后,根据事件类型分配(Dispatch)给某个线程。

单 Reactor 单进程 / 线程

只适用于业务处理非常快速的场景,不适用计算机密集型的场景。需要 handler 尽量快速地执行处理,否则会影响吞吐。

单 Reactor 单进程 / 线程

单 Reactor 多进程 / 线程

事件处理由 worker 线程池负责,解决了单线程下 handler处理耗时导致吞吐降低的问题。
但还存在大并发量情况下 dispatch 速率影响吞吐的问题。

单 Reactor 多进程 / 线程

多(主从) Reactor 多进程 / 线程

Reactor 只负责 dispatch 事件,因此为了充分利用cpu,可以通过多 Reactor 并行 dispatch。
主从 Reactor,主负责建立连接(处理accept),子 Reactor 负责 dispatch 事件到 worker 线程池

多(主从) Reactor 多进程 / 线程
上一篇下一篇

猜你喜欢

热点阅读